<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Cisa Kev | FirewallSync</title><link>https://firewallsync.com/tags/cisa-kev/</link><description>Practical cybersecurity writing for engineering teams that ship fast.</description><language>en-us</language><image><url>https://firewallsync.com/icon-512.png</url><title>FirewallSync</title><link>https://firewallsync.com/</link></image><lastBuildDate>Sat, 07 Nov 2026 04:50:00 +0000</lastBuildDate><atom:link href="https://firewallsync.com/tags/cisa-kev/index.xml" rel="self" type="application/rss+xml"/><item><title>EPSS Scores for KEV CVEs: Why New Entries Score Low</title><link>https://firewallsync.com/posts/epss-scores-for-kev-cves-low-after-listing-threshold-audit/</link><guid>https://firewallsync.com/posts/epss-scores-for-kev-cves-low-after-listing-threshold-audit/</guid><pubDate>Sat, 07 Nov 2026 04:50:00 +0000</pubDate><dc:creator>John Marshall</dc:creator><category>Security Operations</category><description>An EPSS cutoff of 0.1 misses a quarter of CISA's KEV catalog today, and half of the entries added in the last 90 days. We tracked 113 KEV additions through 145 daily EPSS v5 files: the median score on the day of listing was 0.0076, and entries that crossed 0.1 after listing took a median of 14 days. Data, a tested audit script and the rule it implies.</description><enclosure url="https://firewallsync.com/posts/epss-scores-for-kev-cves-low-after-listing-threshold-audit/cover_hu7102096413798716976.jpg" type="image/jpeg" length="0"/><media:content url="https://firewallsync.com/posts/epss-scores-for-kev-cves-low-after-listing-threshold-audit/cover_hu7102096413798716976.jpg" medium="image" type="image/jpeg" width="1200" height="630"/><content:encoded><![CDATA[<p><img src="https://firewallsync.com/posts/epss-scores-for-kev-cves-low-after-listing-threshold-audit/cover_hu7102096413798716976.jpg" alt="A wooden-framed hourglass with grey sand running from the upper bulb into the lower one, photographed against a black background" width="1200" height="630"></p><p>If your patch pipeline uses an EPSS cutoff to decide what is urgent, it will miss about a quarter of CISA&rsquo;s Known Exploited Vulnerabilities (KEV) catalog, and roughly <strong>half of the entries CISA added in the last 90 days</strong>. Joining KEV catalog 2026.11.06 to the EPSS file for November 6, 2026, 436 of 1,768 scored KEV CVEs sit below 0.1. Tracking recent additions day by day shows why: EPSS scores for newly listed CVEs typically start low and take about two weeks to cross 0.1, which is the window in which a KEV listing matters most.</p>
<p>We argued in our <a href="/posts/cvss-vs-epss-vs-kev-which-signal-should-decide-what-you-patch-first/">CVSS vs EPSS vs KEV comparison</a> that KEV should be checked before EPSS. This article measures what happens when it is not, and gives you a script to test your own cutoff.</p>
<h2 id="the-snapshot-kev-cves-below-common-epss-cutoffs">The snapshot: KEV CVEs below common EPSS cutoffs</h2>
<p>Data: the <a href="https://github.com/cisagov/kev-data">KEV JSON</a> (catalog version 2026.11.06, released 2026-11-06T19:44Z, 1,769 entries) and the <a href="https://github.com/empiricalsec/epss_scores">EPSS daily file</a> for November 6, 2026 (<code>model_version:v2026.06.15</code>, 400,985 scored CVEs). One KEV entry, CVE-2026-19868, added that day, had no EPSS score yet.</p>
<table>
  <thead>
      <tr>
          <th>EPSS cutoff</th>
          <th>KEV CVEs below it (of 1,768)</th>
          <th>Share</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>0.5</td>
          <td>861</td>
          <td>48.7%</td>
      </tr>
      <tr>
          <td>0.1</td>
          <td>436</td>
          <td>24.7%</td>
      </tr>
      <tr>
          <td>0.05</td>
          <td>307</td>
          <td>17.4%</td>
      </tr>
      <tr>
          <td>0.01</td>
          <td>54</td>
          <td>3.1%</td>
      </tr>
      <tr>
          <td>90th percentile</td>
          <td>248</td>
          <td>14.0%</td>
      </tr>
      <tr>
          <td>95th percentile</td>
          <td>405</td>
          <td>22.9%</td>
      </tr>
  </tbody>
</table>
<p>The median KEV CVE scores 0.536 (99.0th percentile), so the typical entry is well above any cutoff. The problem is the tail, and the tail is not random. If you are unsure what the <code>epss</code> and <code>percentile</code> columns mean, our <a href="/posts/epss-data-feed-columns-percentile-vs-probability-and-how-to-join-it-to-a-scanner-export/">EPSS data feed guide</a> covers them.</p>
<h2 id="newest-kev-entries-score-lowest">Newest KEV entries score lowest</h2>
<p>Split by <code>dateAdded</code>, the share below 0.1 falls as entries age:</p>
<table>
  <thead>
      <tr>
          <th>Listed in KEV</th>
          <th>KEV CVEs</th>
          <th>Below 0.1</th>
          <th>Share</th>
          <th>Median EPSS</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Within 90 days</td>
          <td>106</td>
          <td>52</td>
          <td>49.1%</td>
          <td>0.104</td>
      </tr>
      <tr>
          <td>91 to 365 days ago</td>
          <td>207</td>
          <td>57</td>
          <td>27.5%</td>
          <td>0.444</td>
      </tr>
      <tr>
          <td>More than 1 year ago</td>
          <td>1,455</td>
          <td>327</td>
          <td>22.5%</td>
          <td>0.583</td>
      </tr>
  </tbody>
</table>
<p>Several recent listings we covered are in that first row. On November 6, WordPress CVE-2026-119977 (added November 3) scored 0.0116, Ivanti CVE-2026-111357 and CVE-2026-118527 (added October 30) scored 0.0053 and 0.0082, and VMware ESX CVE-2026-41709 (added October 28) scored 0.0069. Each would sit below even a 0.05 cutoff.</p>
<h2 id="tracking-the-lag-113-kev-additions-day-by-day">Tracking the lag: 113 KEV additions, day by day</h2>
<p>A snapshot cannot show whether low scores are permanent or just early. So we took the 113 KEV entries added between June 16 and October 7, 2026, and looked up each one in every daily EPSS file from June 15 to November 6 (145 files). That window starts the day after EPSS v5 replaced v4, so every score comes from the same model (<code>v2026.06.15</code>); our <a href="/posts/epss-v5-score-shift-model-version-v2026-06-15/">EPSS v5 analysis</a> explains why mixing models would distort this. The October 7 end date gives every entry at least 30 days of follow-up.</p>
<table>
  <thead>
      <tr>
          <th>Day relative to KEV listing</th>
          <th>Entries with a score</th>
          <th>At or above 0.1</th>
          <th>Share</th>
          <th>Median EPSS</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Day before</td>
          <td>77</td>
          <td>15</td>
          <td>19.5%</td>
          <td>0.0072</td>
      </tr>
      <tr>
          <td>Day of listing</td>
          <td>91</td>
          <td>15</td>
          <td>16.5%</td>
          <td>0.0076</td>
      </tr>
      <tr>
          <td>7 days after</td>
          <td>113</td>
          <td>38</td>
          <td>33.6%</td>
          <td>0.0220</td>
      </tr>
      <tr>
          <td>30 days after</td>
          <td>113</td>
          <td>68</td>
          <td>60.2%</td>
          <td>0.1689</td>
      </tr>
  </tbody>
</table>
<p>Notes on reading it:</p>
<ul>
<li><strong>Missing scores are real.</strong> 36 of the 113 had no EPSS score the day before listing and 22 had none on the day itself, usually because the CVE had only just been published. By day 7 all 113 were scored.</li>
<li><strong>Crossing time.</strong> 15 entries were already at or above 0.1 the day before listing. Of the rest, 64 crossed 0.1 later, after a median of <strong>14 days</strong> (23 within a week). 34 had not reached 0.1 by November 6, 36 to 115 days after listing.</li>
<li><strong>Timing, not cause.</strong> EPSS files publish only the score. We cannot see which inputs moved a score, so we do not claim the KEV listing itself caused any rise.</li>
</ul>
<p>Why the gap exists is consistent with how each list is built. CISA adds a CVE when there is &ldquo;reliable evidence that the vulnerability has been actively exploited in the wild&rdquo; (<a href="https://cisa.gov/known-exploited-vulnerabilities">CISA</a>). EPSS is &ldquo;the probability [0-1] of exploitation in the wild in the next 30 days (following score publication)&rdquo;, estimated from data (<a href="https://github.com/empiricalsec/epss_scores">EPSS scores README</a>). The community <a href="https://riskbasedprioritization.github.io/epss/EPSS_and_CISA_KEV">Risk Based Prioritization guide</a> puts it this way: EPSS reflects &ldquo;the automated data we receive and collect&rdquo;, while KEV is &ldquo;a list of human curated CVEs with historical exploitation activity&rdquo;, so a CVE can be in one and score low in the other. It also quotes FIRST&rsquo;s EPSS FAQ: where exploitation is evidenced, &ldquo;that information should supersede anything EPSS has to say&rdquo;.</p>
<h2 id="what-to-change-in-a-pipeline">What to change in a pipeline</h2>
<p>These are our recommendations from the data above; we have not measured them on anyone&rsquo;s backlog.</p>
<ol>
<li><strong>Route on KEV membership before any EPSS rule.</strong> A listed CVE goes to the urgent queue whatever its score. This is the first step in our <a href="/posts/vulnerability-prioritization-workflow-cve-record-to-patch-ticket/">prioritization workflow</a>.</li>
<li><strong>Do not &ldquo;promote&rdquo; KEV entries by adding a bonus to the EPSS score.</strong> A bonus sized for the median KEV entry (0.536) does nothing for an entry at 0.005.</li>
<li><strong>Treat a missing EPSS score as &ldquo;unknown&rdquo;, not zero.</strong> 22 of the 113 entries had no score on the day they were listed. A join that fills nulls with 0 drops exactly those.</li>
<li><strong>Refresh KEV more often than you refresh thresholds.</strong> The <a href="/posts/cisa-kev-json-feed-what-each-field-means-and-how-to-match-it-against-a-scanner-export/">KEV JSON feed</a> updates on listing days; the lag above means the KEV check is doing nearly all of the work for the first two weeks.</li>
<li><strong>Re-run the audit after every EPSS model change</strong>, since cutoffs shift with the model.</li>
</ol>
<h2 id="audit-your-own-cutoff">Audit your own cutoff</h2>
<p>This script joins the two public files and reports how many KEV entries your cutoff misses, by listing age. We ran it with Python 3.12 on the files named above; the output below is for 0.1.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env python3</span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;How many CISA KEV entries would an EPSS cutoff miss?
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">usage: kev_epss_audit.py known_exploited_vulnerabilities.json epss_scores-YYYY-MM-DD.csv.gz CUTOFF&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> csv<span style="color:#f92672">,</span> gzip<span style="color:#f92672">,</span> json<span style="color:#f92672">,</span> sys
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> datetime <span style="color:#f92672">import</span> date
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>kev_path, epss_path, cutoff <span style="color:#f92672">=</span> sys<span style="color:#f92672">.</span>argv[<span style="color:#ae81ff">1</span>], sys<span style="color:#f92672">.</span>argv[<span style="color:#ae81ff">2</span>], float(sys<span style="color:#f92672">.</span>argv[<span style="color:#ae81ff">3</span>])
</span></span><span style="display:flex;"><span>catalog <span style="color:#f92672">=</span> json<span style="color:#f92672">.</span>load(open(kev_path))
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">with</span> gzip<span style="color:#f92672">.</span>open(epss_path, <span style="color:#e6db74">&#34;rt&#34;</span>) <span style="color:#66d9ef">as</span> f:
</span></span><span style="display:flex;"><span>    header <span style="color:#f92672">=</span> f<span style="color:#f92672">.</span>readline()<span style="color:#f92672">.</span>strip()                 <span style="color:#75715e"># &#34;#model_version:...,score_date:...&#34;</span>
</span></span><span style="display:flex;"><span>    scores <span style="color:#f92672">=</span> {r[<span style="color:#e6db74">&#34;cve&#34;</span>]: float(r[<span style="color:#e6db74">&#34;epss&#34;</span>]) <span style="color:#66d9ef">for</span> r <span style="color:#f92672">in</span> csv<span style="color:#f92672">.</span>DictReader(f)}
</span></span><span style="display:flex;"><span>score_day <span style="color:#f92672">=</span> date<span style="color:#f92672">.</span>fromisoformat(header<span style="color:#f92672">.</span>split(<span style="color:#e6db74">&#34;score_date:&#34;</span>)[<span style="color:#ae81ff">1</span>][:<span style="color:#ae81ff">10</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;KEV catalog </span><span style="color:#e6db74">{</span>catalog[<span style="color:#e6db74">&#39;catalogVersion&#39;</span>]<span style="color:#e6db74">}</span><span style="color:#e6db74">, EPSS </span><span style="color:#e6db74">{</span>header[<span style="color:#ae81ff">1</span>:]<span style="color:#e6db74">}</span><span style="color:#e6db74">, cutoff </span><span style="color:#e6db74">{</span>cutoff<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>buckets <span style="color:#f92672">=</span> {<span style="color:#e6db74">&#34;added &lt;= 90 days ago&#34;</span>: [], <span style="color:#e6db74">&#34;91-365 days&#34;</span>: [], <span style="color:#e6db74">&#34;over 1 year&#34;</span>: []}
</span></span><span style="display:flex;"><span>unscored <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> v <span style="color:#f92672">in</span> catalog[<span style="color:#e6db74">&#34;vulnerabilities&#34;</span>]:
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> v[<span style="color:#e6db74">&#34;cveID&#34;</span>] <span style="color:#f92672">not</span> <span style="color:#f92672">in</span> scores:
</span></span><span style="display:flex;"><span>        unscored<span style="color:#f92672">.</span>append(v[<span style="color:#e6db74">&#34;cveID&#34;</span>])
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">continue</span>
</span></span><span style="display:flex;"><span>    age <span style="color:#f92672">=</span> (score_day <span style="color:#f92672">-</span> date<span style="color:#f92672">.</span>fromisoformat(v[<span style="color:#e6db74">&#34;dateAdded&#34;</span>]))<span style="color:#f92672">.</span>days
</span></span><span style="display:flex;"><span>    name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;added &lt;= 90 days ago&#34;</span> <span style="color:#66d9ef">if</span> age <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">90</span> <span style="color:#66d9ef">else</span> <span style="color:#e6db74">&#34;91-365 days&#34;</span> <span style="color:#66d9ef">if</span> age <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">365</span> <span style="color:#66d9ef">else</span> <span style="color:#e6db74">&#34;over 1 year&#34;</span>
</span></span><span style="display:flex;"><span>    buckets[name]<span style="color:#f92672">.</span>append((v[<span style="color:#e6db74">&#34;cveID&#34;</span>], scores[v[<span style="color:#e6db74">&#34;cveID&#34;</span>]]))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>total <span style="color:#f92672">=</span> below <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> name, rows <span style="color:#f92672">in</span> buckets<span style="color:#f92672">.</span>items():
</span></span><span style="display:flex;"><span>    n_below <span style="color:#f92672">=</span> sum(<span style="color:#ae81ff">1</span> <span style="color:#66d9ef">for</span> _, s <span style="color:#f92672">in</span> rows <span style="color:#66d9ef">if</span> s <span style="color:#f92672">&lt;</span> cutoff)
</span></span><span style="display:flex;"><span>    total <span style="color:#f92672">+=</span> len(rows); below <span style="color:#f92672">+=</span> n_below
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">{</span>name<span style="color:#e6db74">:</span><span style="color:#e6db74">22</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> </span><span style="color:#e6db74">{</span>len(rows)<span style="color:#e6db74">:</span><span style="color:#e6db74">5</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> KEV CVEs, </span><span style="color:#e6db74">{</span>n_below<span style="color:#e6db74">:</span><span style="color:#e6db74">4</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> below cutoff (</span><span style="color:#e6db74">{</span>n_below<span style="color:#f92672">/</span>len(rows)<span style="color:#e6db74">:</span><span style="color:#e6db74">.1%</span><span style="color:#e6db74">}</span><span style="color:#e6db74">)&#34;</span>)
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">{</span><span style="color:#e6db74">&#39;all scored&#39;</span><span style="color:#e6db74">:</span><span style="color:#e6db74">22</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> </span><span style="color:#e6db74">{</span>total<span style="color:#e6db74">:</span><span style="color:#e6db74">5</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> KEV CVEs, </span><span style="color:#e6db74">{</span>below<span style="color:#e6db74">:</span><span style="color:#e6db74">4</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> below cutoff (</span><span style="color:#e6db74">{</span>below<span style="color:#f92672">/</span>total<span style="color:#e6db74">:</span><span style="color:#e6db74">.1%</span><span style="color:#e6db74">}</span><span style="color:#e6db74">)&#34;</span>)
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;not in EPSS file: </span><span style="color:#e6db74">{</span>len(unscored)<span style="color:#e6db74">}</span><span style="color:#e6db74"> </span><span style="color:#e6db74">{</span>unscored[:<span style="color:#ae81ff">5</span>]<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>$ python3 kev_epss_audit.py known_exploited_vulnerabilities.json epss_scores-2026-11-06.csv.gz 0.1
</span></span><span style="display:flex;"><span>KEV catalog 2026.11.06, EPSS model_version:v2026.06.15,score_date:2026-11-06T12:00:23Z, cutoff 0.1
</span></span><span style="display:flex;"><span>added &lt;= 90 days ago     106 KEV CVEs,   52 below cutoff (49.1%)
</span></span><span style="display:flex;"><span>91-365 days              207 KEV CVEs,   57 below cutoff (27.5%)
</span></span><span style="display:flex;"><span>over 1 year             1455 KEV CVEs,  327 below cutoff (22.5%)
</span></span><span style="display:flex;"><span>all scored              1768 KEV CVEs,  436 below cutoff (24.7%)
</span></span><span style="display:flex;"><span>not in EPSS file: 1 [&#39;CVE-2026-19868&#39;]
</span></span></code></pre></div><p>Get the KEV file from <code>raw.githubusercontent.com/cisagov/kev-data/main/known_exploited_vulnerabilities.json</code> and the EPSS file from the <code>empiricalsec/epss_scores</code> repository (one gzip file per day under the year folder). The script reads the whole EPSS file into memory, which is fine for about 400,000 rows.</p>
<h2 id="limits-of-this-article">Limits of this article</h2>
<p>The snapshot is one day (November 6, 2026). The day-by-day sample is 113 entries over one model version, which is enough to show a pattern but not to estimate a precise lag for any vendor or CVE type. &ldquo;Day of listing&rdquo; compares KEV <code>dateAdded</code> to the EPSS file of the same date; their publication times within the day differ. We did not examine why the 34 entries that stayed below 0.1 scored as they did.</p>
<h2 id="sources-and-verification">Sources and verification</h2>
<table>
  <thead>
      <tr>
          <th>Important claim</th>
          <th>Source</th>
          <th>Status</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>KEV counts, below-cutoff shares, medians, age buckets, per-CVE scores</td>
          <td><a href="https://github.com/cisagov/kev-data">cisagov/kev-data</a> catalog 2026.11.06 joined to <a href="https://github.com/empiricalsec/epss_scores">empiricalsec/epss_scores</a> 2026-11-06, computed with the script above</td>
          <td>Verified (computed)</td>
      </tr>
      <tr>
          <td>113-entry day-by-day results (medians, shares, 14-day median crossing, 34 never)</td>
          <td>Same KEV file and 145 daily EPSS files 2026-06-15 to 2026-11-06, all <code>v2026.06.15</code>, computed in Python</td>
          <td>Verified (computed)</td>
      </tr>
      <tr>
          <td>EPSS column definitions and 30-day window</td>
          <td><a href="https://github.com/empiricalsec/epss_scores">EPSS scores README</a></td>
          <td>Verified</td>
      </tr>
      <tr>
          <td>KEV inclusion criterion</td>
          <td><a href="https://cisa.gov/known-exploited-vulnerabilities">CISA KEV catalog page</a></td>
          <td>Verified, Nov 7, 2026</td>
      </tr>
      <tr>
          <td>Data-generating difference between EPSS and KEV; FIRST FAQ &ldquo;supersede&rdquo; quote</td>
          <td><a href="https://riskbasedprioritization.github.io/epss/EPSS_and_CISA_KEV">Risk Based Prioritization guide</a> (community guide quoting FIRST)</td>
          <td>Qualified: secondary for the FIRST quote</td>
      </tr>
  </tbody>
</table>
]]></content:encoded></item></channel></rss>