Files
vulncheck/tools/check_eol_slugs.py
T
vulncheck fdd1019fbd fix(eol): correct the endoflife.date slugs — seven pointed at nothing
The product mapping was hand-written and never checked against the actual
catalogue, so a quarter of it was wrong. Every EOL run asked for ms-office,
exchange-server, java, visual-cpp, adobe-acrobat, apache and iis, and got 404
back each time — visible in the log as "not in catalog", invisible everywhere
else. The scan simply found no lifecycle data and moved on, which is why
Microsoft Exchange Server 2016 came out looking supported.

Checked against the live list (462 products) rather than guessed:

  exchange-server -> msexchange           (the reported Exchange 2016 case)
  ms-office       -> office
  apache          -> apache-http-server
  java            -> oracle-jdk

The other three do not exist there at all and never will: endoflife.date has
no record for Adobe Acrobat, the Visual C++ redistributables, or IIS. Their 18
mapping entries are removed rather than left to 404 on every run — IIS ships
with Windows Server and is covered by that entry, the other two depend on the
Microsoft export or a vendor page. Same reasoning as the existing Edge note.

tools/check_eol_slugs.py verifies the whole mapping against the live catalogue
and exits non-zero when a slug disappears, so this cannot rot again unnoticed.
2026-08-10 09:34:48 +02:00

59 lines
2.2 KiB
Python

"""Do our endoflife.date slugs still exist in their catalogue?
The mapping was hand-written and never checked against the real product list,
so seven of 33 slugs pointed at nothing: every EOL run asked for ms-office,
exchange-server, java, visual-cpp and adobe-acrobat and got a 404 back. The
misses were invisible in the GUI — the scan just found no lifecycle data and
moved on, so Exchange Server 2016 looked supported.
docker compose exec backend python tools/check_eol_slugs.py
Exits non-zero when a slug has disappeared, so it can be run after an upgrade
or on a schedule. Catalogue changes are a fact of life; silently 404ing is not.
"""
import sys
sys.path.insert(0, "/app")
def main():
import httpx
from app.services.eol_service import _PRODUCT_SLUGS, _API_BASE
catalogue_url = _API_BASE.rstrip("/") + "/"
try:
r = httpx.get(catalogue_url, timeout=30.0)
r.raise_for_status()
payload = r.json()
except Exception as e:
print(f"Could not fetch the catalogue ({catalogue_url}): {e}")
return 2
entries = payload.get("result") or payload
catalogue = {p.get("name") if isinstance(p, dict) else p for p in entries}
print(f"endoflife.date lists {len(catalogue)} products.\n")
used = sorted({s for s in _PRODUCT_SLUGS.values() if s})
missing = [s for s in used if s not in catalogue]
for slug in used:
names = sorted(k for k, v in _PRODUCT_SLUGS.items() if v == slug)
mark = "GONE" if slug in missing else " ok "
print(f" [{mark}] {slug:24} ({len(names)} name(s) map here)")
print()
if missing:
import difflib
print(f"{len(missing)} slug(s) no longer exist — every lookup 404s:")
for slug in missing:
near = difflib.get_close_matches(slug, catalogue, n=3, cutoff=0.55)
print(f" {slug:24} closest in catalogue: {near or 'nothing similar'}")
return 1
print(f"All {len(used)} slugs resolve. Products deliberately left unmapped "
f"(Acrobat, VC++ redistributables, IIS, Edge) are not listed here — "
f"endoflife.date has no record for them.")
return 0
if __name__ == "__main__":
sys.exit(main())