<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: end-of-life.org</title>
    <description>The latest articles on DEV Community by end-of-life.org (@endoflifeorg).</description>
    <link>https://dev.to/endoflifeorg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4073249%2F30cbcb9e-1f68-4aba-b918-fecb3ba154af.png</url>
      <title>DEV Community: end-of-life.org</title>
      <link>https://dev.to/endoflifeorg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/endoflifeorg"/>
    <language>en</language>
    <item>
      <title>73% of the Software Versions We Track Are Already End of Life</title>
      <dc:creator>end-of-life.org</dc:creator>
      <pubDate>Thu, 13 Aug 2026 05:30:00 +0000</pubDate>
      <link>https://dev.to/endoflifeorg/73-of-the-software-versions-we-track-are-already-end-of-life-2e2o</link>
      <guid>https://dev.to/endoflifeorg/73-of-the-software-versions-we-track-are-already-end-of-life-2e2o</guid>
      <description>&lt;p&gt;Of the 8,307 software and hardware versions in&lt;br&gt;
&lt;a href="https://end-of-life.org/" rel="noopener noreferrer"&gt;end-of-life.org&lt;/a&gt;, 73% are past their end-of-life date.&lt;br&gt;
Another 142 branches reach EOL within the next 90 days.&lt;/p&gt;

&lt;p&gt;The honest reading of that first number is: it is not very interesting. Software&lt;br&gt;
accumulates dead versions the way a filesystem accumulates old files. Python 2.7&lt;br&gt;
being EOL is not news, and nobody's risk register needs a line item for it.&lt;/p&gt;

&lt;p&gt;The number that matters is much smaller, and getting to it means throwing away most&lt;br&gt;
of the 73%.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"73% end of life" is mostly historical sediment — versions nobody runs. The
figure is a denominator problem, not a finding.&lt;/li&gt;
&lt;li&gt;What matters is the intersection of &lt;em&gt;dead&lt;/em&gt; and &lt;em&gt;still deployed&lt;/em&gt;, which no public
dataset knows for you. Only your inventory does.&lt;/li&gt;
&lt;li&gt;Among branches that are both dead and plausibly in production, rank by critical
CVE count and by accumulation rate, not by total CVEs.&lt;/li&gt;
&lt;li&gt;The 90-day window is the more useful number to put on a roadmap: it is the only
part of the dataset that is actionable before it becomes urgent.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Most of that 73% is sediment
&lt;/h2&gt;

&lt;p&gt;Every product carries its whole history. MySQL has 18 tracked branches, Go has 27,&lt;br&gt;
PHP has 23, PostgreSQL has 29. The overwhelming majority of those are ancient and&lt;br&gt;
uninteresting, and they will stay in the denominator forever, pushing the percentage&lt;br&gt;
up every year regardless of what anyone does.&lt;/p&gt;

&lt;p&gt;So the number does not describe risk. It describes how long the industry has been&lt;br&gt;
shipping software.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://end-of-life.org/products/" rel="noopener noreferrer"&gt;full directory&lt;/a&gt; covers 462 products, and&lt;br&gt;
browsing it makes the shape obvious: for any given product, a couple of branches are&lt;br&gt;
maintained and a long tail behind them is not. That tail is normal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number that isn't sediment
&lt;/h2&gt;

&lt;p&gt;The intersection you care about is &lt;strong&gt;dead and still running in your environment.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That second half is the part no external dataset can answer. A lifecycle database&lt;br&gt;
knows when Oracle stopped patching MySQL 5.7. It has no idea whether you have four&lt;br&gt;
of them in a VPC somewhere. Every EOL dashboard that shows you a big scary&lt;br&gt;
percentage without reading your inventory is showing you the sediment.&lt;/p&gt;

&lt;p&gt;Which means the useful workflow runs in the opposite direction from how these tools&lt;br&gt;
are usually pitched:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enumerate what you actually run — lockfiles, container images, AMIs, whatever is
authoritative in your environment.&lt;/li&gt;
&lt;li&gt;Look each one up against lifecycle data.&lt;/li&gt;
&lt;li&gt;Rank only the matches.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 3 is where most of the value is, and where most teams get the ordering wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ranking: the trap is sorting by CVE count
&lt;/h2&gt;

&lt;p&gt;The obvious sort is "how many vulnerabilities has this version missed since it went&lt;br&gt;
EOL." It is the wrong primary key, and the current data shows why cleanly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://end-of-life.org/mysql/5.7/" rel="noopener noreferrer"&gt;MySQL 5.7&lt;/a&gt; sits at the top of that list with&lt;br&gt;
127 missed CVEs. &lt;strong&gt;Zero of them are rated critical.&lt;/strong&gt; Go 1.15 sits just below it&lt;br&gt;
with 122 missed CVEs, and &lt;strong&gt;twelve of those are critical.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sort descending by volume and you fund the wrong upgrade first. I went through the&lt;br&gt;
full ranking and the arithmetic behind it in&lt;br&gt;
&lt;a href="https://dev.to/endoflifeorg/your-eol-risk-list-is-probably-sorted-wrong-1po7"&gt;Your EOL Risk List Is Probably Sorted Wrong&lt;/a&gt;;&lt;br&gt;
the short version is below.&lt;/p&gt;

&lt;p&gt;There is a second distortion in the same column: CVE count rewards age. Normalise by&lt;br&gt;
time and the ranking inverts.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Days EOL&lt;/th&gt;
&lt;th&gt;CVEs missed&lt;/th&gt;
&lt;th&gt;Critical&lt;/th&gt;
&lt;th&gt;CVEs/year&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MySQL 5.7&lt;/td&gt;
&lt;td&gt;~1,060&lt;/td&gt;
&lt;td&gt;127&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~44&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL 8.1&lt;/td&gt;
&lt;td&gt;1,020&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;~37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go 1.17&lt;/td&gt;
&lt;td&gt;1,469&lt;/td&gt;
&lt;td&gt;106&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;~26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go 1.16&lt;/td&gt;
&lt;td&gt;1,609&lt;/td&gt;
&lt;td&gt;111&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;~25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go 1.15&lt;/td&gt;
&lt;td&gt;1,820&lt;/td&gt;
&lt;td&gt;122&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;~24&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Go 1.15 had nearly twice as long as MySQL 5.7 to accumulate a slightly smaller&lt;br&gt;
number. The Go branches cluster at 24-26 missed CVEs per year, the MySQL branches at&lt;br&gt;
37-44 — the same measurement, from the same source, separating the two ecosystems by&lt;br&gt;
roughly 50%. Counts published by the &lt;a href="https://nvd.nist.gov/" rel="noopener noreferrer"&gt;NVD&lt;/a&gt;; lifecycle dates&lt;br&gt;
from vendor policy documents such as Oracle's&lt;br&gt;
&lt;a href="https://www.oracle.com/us/support/library/lifetime-support-technology-069183.pdf" rel="noopener noreferrer"&gt;Lifetime Support Policy&lt;/a&gt;&lt;br&gt;
and Go's &lt;a href="https://go.dev/doc/devel/release#policy" rel="noopener noreferrer"&gt;release policy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That rate is the number to bring to a planning conversation, because it answers the&lt;br&gt;
question that actually gets asked: &lt;em&gt;what does it cost us to defer this another&lt;br&gt;
quarter?&lt;/em&gt; Total CVE count answers a question nobody asked, which is how long the&lt;br&gt;
thing has been dead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 90-day window is the part you can act on
&lt;/h2&gt;

&lt;p&gt;142 branches reach end of life within 90 days.&lt;/p&gt;

&lt;p&gt;Unlike the 73%, this number is small, forward-looking, and maps directly onto a&lt;br&gt;
planning cycle. Everything in it is by definition something a vendor is still&lt;br&gt;
supporting today, which means the upgrade path exists, is documented, and has not&lt;br&gt;
yet acquired the compatibility debt that accumulates after a branch goes cold.&lt;/p&gt;

&lt;p&gt;Upgrades scheduled before the EOL date are maintenance. The same upgrades six months&lt;br&gt;
later are incidents. The window is the cheapest part of the dataset to act on and&lt;br&gt;
the easiest to ignore, because nothing is broken yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would put on the roadmap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Sort by&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Now&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dead &lt;strong&gt;and&lt;/strong&gt; deployed&lt;/td&gt;
&lt;td&gt;Critical CVE count&lt;/td&gt;
&lt;td&gt;Severity is the only column that maps to blast radius&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;This quarter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dead and deployed, high accrual rate&lt;/td&gt;
&lt;td&gt;CVEs/year&lt;/td&gt;
&lt;td&gt;Where the next critical shows up, before it does&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Next two quarters&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The 90-day list&lt;/td&gt;
&lt;td&gt;EOL date&lt;/td&gt;
&lt;td&gt;Cheapest window — it is maintenance now, remediation later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Never&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The rest of the 73%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Sediment. Treating it as a backlog is how a risk register becomes something nobody reads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The only hard part is the first column of row one, and it does not come from any&lt;br&gt;
lifecycle database. It comes from your inventory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two caveats worth stating plainly
&lt;/h2&gt;

&lt;p&gt;Vulnerability analysis currently covers 13 products — the runtimes, databases and&lt;br&gt;
app servers where post-EOL exposure is easiest to reason about. The other 449&lt;br&gt;
products in the directory carry lifecycle dates only. If a field comes back empty&lt;br&gt;
for those, it means &lt;em&gt;not analysed&lt;/em&gt;, not &lt;em&gt;no vulnerabilities&lt;/em&gt;, and any dashboard that&lt;br&gt;
renders it as zero is lying to you.&lt;/p&gt;

&lt;p&gt;And &lt;a href="https://www.first.org/cvss/" rel="noopener noreferrer"&gt;CVSS&lt;/a&gt; severity is a scoring model, not a measure&lt;br&gt;
of exploitability in your environment. "Zero criticals" does not mean MySQL 5.7 is&lt;br&gt;
fine — 127 unpatched vulnerabilities is a real number. It means that between two&lt;br&gt;
upgrades competing for the same window, severity is a better tiebreaker than volume.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Lifecycle dates are read from vendor policy documents and release notes;&lt;br&gt;
vulnerability data comes from the &lt;a href="https://nvd.nist.gov/" rel="noopener noreferrer"&gt;NVD&lt;/a&gt; API. The per-product&lt;br&gt;
source list is published on &lt;a href="https://end-of-life.org/data-sources/" rel="noopener noreferrer"&gt;data sources&lt;/a&gt;, so&lt;br&gt;
every figure here is reproducible. Not endorsed or certified by the NVD, and not&lt;br&gt;
affiliated with any vendor — &lt;a href="https://end-of-life.org/about/" rel="noopener noreferrer"&gt;about&lt;/a&gt; ·&lt;br&gt;
&lt;a href="https://end-of-life.org/contact/" rel="noopener noreferrer"&gt;contact&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>programming</category>
      <category>database</category>
    </item>
    <item>
      <title>Your EOL Risk List Is Probably Sorted Wrong</title>
      <dc:creator>end-of-life.org</dc:creator>
      <pubDate>Wed, 12 Aug 2026 03:54:10 +0000</pubDate>
      <link>https://dev.to/endoflifeorg/your-eol-risk-list-is-probably-sorted-wrong-1po7</link>
      <guid>https://dev.to/endoflifeorg/your-eol-risk-list-is-probably-sorted-wrong-1po7</guid>
      <description>&lt;p&gt;Every "upgrade this now" list I have seen ranks end-of-life software the same way:&lt;br&gt;
count the CVEs published since the version stopped receiving fixes, sort descending,&lt;br&gt;
start at the top.&lt;/p&gt;

&lt;p&gt;That ordering is easy to compute and it is misleading.&lt;/p&gt;

&lt;p&gt;Here is the current top of that list, pulled from&lt;br&gt;
&lt;a href="https://end-of-life.org/" rel="noopener noreferrer"&gt;end-of-life.org&lt;/a&gt;, which joins vendor EOL dates against&lt;br&gt;
the &lt;a href="https://nvd.nist.gov/" rel="noopener noreferrer"&gt;NVD&lt;/a&gt; and counts what each dead branch never received:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;EOL since&lt;/th&gt;
&lt;th&gt;CVEs missed&lt;/th&gt;
&lt;th&gt;Rated critical&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MySQL 5.7&lt;/td&gt;
&lt;td&gt;Oct 2023&lt;/td&gt;
&lt;td&gt;127&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go 1.15&lt;/td&gt;
&lt;td&gt;Aug 2021&lt;/td&gt;
&lt;td&gt;122&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;12&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go 1.16&lt;/td&gt;
&lt;td&gt;Mar 2022&lt;/td&gt;
&lt;td&gt;111&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go 1.17&lt;/td&gt;
&lt;td&gt;Aug 2022&lt;/td&gt;
&lt;td&gt;106&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL 8.1&lt;/td&gt;
&lt;td&gt;Oct 2023&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The version at the top of the list by CVE count has zero critical CVEs. The one
below it has twelve.&lt;/li&gt;
&lt;li&gt;CVE count conflates two different things: how long the branch has been dead, and
how fast vulnerabilities accumulate in that ecosystem.&lt;/li&gt;
&lt;li&gt;Normalising by time changes the ranking. MySQL accrues missed CVEs roughly 50%
faster per year than Go does.&lt;/li&gt;
&lt;li&gt;Severity and rate are both in the data already. Neither shows up in a plain
descending sort.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  The number one entry has no critical vulnerabilities
&lt;/h2&gt;

&lt;p&gt;MySQL 5.7 has missed 127 CVEs since October 2023. Zero of them are rated critical.&lt;/p&gt;

&lt;p&gt;Go 1.15 has missed 122 — five fewer — and twelve of those are critical.&lt;/p&gt;

&lt;p&gt;If you are triaging with a fixed budget and you work top-down, you spend it on the&lt;br&gt;
MySQL upgrade first. The data in the same table says the Go upgrade is the one&lt;br&gt;
carrying critical-severity exposure.&lt;/p&gt;

&lt;p&gt;I am not claiming MySQL 5.7 is safe. 127 unpatched vulnerabilities is 127&lt;br&gt;
unpatched vulnerabilities, and &lt;a href="https://www.first.org/cvss/" rel="noopener noreferrer"&gt;CVSS&lt;/a&gt; scores are a&lt;br&gt;
severity model, not a measure of exploitability in your environment. The point is&lt;br&gt;
narrower: &lt;strong&gt;the sort key is throwing away the severity column.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Volume also measures how long the corpse has been sitting there
&lt;/h2&gt;

&lt;p&gt;The second problem is that CVE count rewards age.&lt;/p&gt;

&lt;p&gt;Go 1.15 went EOL in August 2021. It has been unpatched for 1,820 days. MySQL 5.7&lt;br&gt;
went EOL in October 2023, roughly 1,060 days ago. Go 1.15 had almost twice as long&lt;br&gt;
to accumulate its 122.&lt;/p&gt;

&lt;p&gt;Divide by time and the picture inverts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Days EOL&lt;/th&gt;
&lt;th&gt;CVEs missed&lt;/th&gt;
&lt;th&gt;CVEs/year&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MySQL 5.7&lt;/td&gt;
&lt;td&gt;~1,060&lt;/td&gt;
&lt;td&gt;127&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~44&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL 8.1&lt;/td&gt;
&lt;td&gt;1,020&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;~37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go 1.17&lt;/td&gt;
&lt;td&gt;1,469&lt;/td&gt;
&lt;td&gt;106&lt;/td&gt;
&lt;td&gt;~26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go 1.16&lt;/td&gt;
&lt;td&gt;1,609&lt;/td&gt;
&lt;td&gt;111&lt;/td&gt;
&lt;td&gt;~25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go 1.15&lt;/td&gt;
&lt;td&gt;1,820&lt;/td&gt;
&lt;td&gt;122&lt;/td&gt;
&lt;td&gt;~24&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Go branches cluster tightly around 24-26 CVEs per year. The MySQL branches sit&lt;br&gt;
at 37-44. Same measurement, same source, and the two ecosystems separate cleanly.&lt;br&gt;
Staying one more year on an EOL MySQL branch costs you roughly 50% more missed&lt;br&gt;
fixes than staying one more year on an EOL Go branch.&lt;/p&gt;

&lt;p&gt;That is a forecasting number, which is what you actually want when the question is&lt;br&gt;
"can this upgrade wait another quarter." Raw CVE count is a backward-looking number&lt;br&gt;
dressed up as a risk score.&lt;/p&gt;

&lt;p&gt;Details for any single branch — the full timeline, the per-year CVE breakdown, and&lt;br&gt;
the individual CVEs with their CVSS scores — are on the version pages, for example&lt;br&gt;
&lt;a href="https://end-of-life.org/mysql/5.7/" rel="noopener noreferrer"&gt;MySQL 5.7&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why the two ecosystems differ this much
&lt;/h2&gt;

&lt;p&gt;Honestly: I do not know, and neither does the data.&lt;/p&gt;

&lt;p&gt;Plausible contributors are that MySQL advisories arrive in Oracle's quarterly&lt;br&gt;
Critical Patch Update batches under its&lt;br&gt;
&lt;a href="https://www.oracle.com/us/support/library/lifetime-support-technology-069183.pdf" rel="noopener noreferrer"&gt;Lifetime Support Policy&lt;/a&gt;,&lt;br&gt;
that Go's &lt;a href="https://go.dev/doc/devel/release#policy" rel="noopener noreferrer"&gt;release policy&lt;/a&gt; — each major&lt;br&gt;
version supported until two newer ones ship — produces a different branch cadence&lt;br&gt;
entirely, and that NVD attribution practices differ by vendor. Any of those could&lt;br&gt;
produce this gap without either project being less safe than the other.&lt;/p&gt;

&lt;p&gt;What I will defend is the narrower claim: &lt;strong&gt;if you are ranking branches, rank them&lt;br&gt;
within an ecosystem, or normalise by time, or both.&lt;/strong&gt; Comparing a raw MySQL count&lt;br&gt;
against a raw Go count and calling the bigger one riskier is comparing two different&lt;br&gt;
measurements.&lt;/p&gt;
&lt;h2&gt;
  
  
  Computing this yourself
&lt;/h2&gt;

&lt;p&gt;The data is a free JSON API — no key, no signup, no rate limit, CORS open, rebuilt&lt;br&gt;
daily. &lt;code&gt;GET /api/v1/products/{name}.json&lt;/code&gt; returns every branch of a product:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://end-of-life.org/api/v1/products/mysql.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dead&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;releases&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isEol&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cvesMissedSinceEol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// eolFrom is either YYYY-MM-DD or YYYY-MM; Date.parse handles both&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;years&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eolFrom&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;365.25&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;864&lt;/span&gt;&lt;span class="nx"&gt;e5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;missed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cvesMissedSinceEol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;perYear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cvesMissedSinceEol&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;years&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perYear&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perYear&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dead&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fields worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;isEol&lt;/code&gt; / &lt;code&gt;eolFrom&lt;/code&gt; — whether the branch is dead and when it died. There are also
&lt;code&gt;eoasFrom&lt;/code&gt; (active support ended) and &lt;code&gt;eoesFrom&lt;/code&gt; (extended support ended); not
every vendor defines all three, and a phase the vendor does not publish comes back
&lt;code&gt;null&lt;/code&gt; rather than a guess.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cvesMissedSinceEol&lt;/code&gt; — the count this whole post is about. It is &lt;code&gt;null&lt;/code&gt; for
products without CVE analysis, which matters (see below).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hasCveAnalysis&lt;/code&gt; on the product object — check this before you trust a &lt;code&gt;null&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dropping that into a CI job that reads your lockfiles and flags branches above a&lt;br&gt;
per-year threshold is maybe an afternoon of work, and it gives you a number you can&lt;br&gt;
put in front of whoever approves the upgrade. Full endpoint docs are at&lt;br&gt;
&lt;a href="https://end-of-life.org/api/" rel="noopener noreferrer"&gt;end-of-life.org/api&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limit worth stating
&lt;/h2&gt;

&lt;p&gt;CVE analysis currently covers 13 products — Django, VMware ESXi, Go, Keycloak,&lt;br&gt;
MySQL, Node.js, PHP, PostgreSQL, Python, Ruby on Rails, Ruby, Spring Boot, Apache&lt;br&gt;
Tomcat. The directory tracks 462 products and 8,307 versions, but for the other 449&lt;br&gt;
you get lifecycle dates only, and &lt;code&gt;cvesMissedSinceEol&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So this method works today for the layer of your stack most likely to be a runtime&lt;br&gt;
or a database, and not for everything else. A &lt;code&gt;null&lt;/code&gt; there means "not analysed,"&lt;br&gt;
not "no CVEs" — do not let a dashboard render it as zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would actually sort by
&lt;/h2&gt;

&lt;p&gt;Three columns, in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Critical count.&lt;/strong&gt; Twelve criticals beats 127 mediums for where the first
upgrade window goes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CVEs per year.&lt;/strong&gt; Forward-looking. Answers "what does one more quarter cost."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total missed.&lt;/strong&gt; Useful context, terrible primary key.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The data for all three is sitting in the same response. It is only the sort that&lt;br&gt;
needs fixing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Lifecycle dates are read from vendor sources — for the two products above, Oracle's&lt;br&gt;
&lt;a href="https://www.oracle.com/us/support/library/lifetime-support-technology-069183.pdf" rel="noopener noreferrer"&gt;Lifetime Support Policy&lt;/a&gt;&lt;br&gt;
and &lt;a href="https://dev.mysql.com/doc/relnotes/mysql/8.4/en/" rel="noopener noreferrer"&gt;MySQL release notes&lt;/a&gt;, and Go's&lt;br&gt;
&lt;a href="https://go.dev/doc/devel/release" rel="noopener noreferrer"&gt;release history&lt;/a&gt;. Vulnerability data comes from the&lt;br&gt;
&lt;a href="https://nvd.nist.gov/" rel="noopener noreferrer"&gt;NVD&lt;/a&gt; API. Every date and count in this post is reproducible from&lt;br&gt;
those sources plus the JSON API; the full source list per product is on&lt;br&gt;
&lt;a href="https://end-of-life.org/data-sources/" rel="noopener noreferrer"&gt;data sources&lt;/a&gt;. This project is not endorsed or&lt;br&gt;
certified by the NVD and is not affiliated with any vendor —&lt;br&gt;
&lt;a href="https://end-of-life.org/about/" rel="noopener noreferrer"&gt;about&lt;/a&gt; · &lt;a href="https://end-of-life.org/contact/" rel="noopener noreferrer"&gt;contact&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>database</category>
      <category>go</category>
    </item>
  </channel>
</rss>
