Skip to content

Add eol_check.py — lifecycle / End-of-Life intel tool (endoflife.date, pure-stdlib)#91

Open
venkatas wants to merge 1 commit into
shuvonsec:mainfrom
venkatas:feat/eol-check-lifecycle-intel
Open

Add eol_check.py — lifecycle / End-of-Life intel tool (endoflife.date, pure-stdlib)#91
venkatas wants to merge 1 commit into
shuvonsec:mainfrom
venkatas:feat/eol-check-lifecycle-intel

Conversation

@venkatas

@venkatas venkatas commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

What this adds

tools/eol_check.py — a small, pure-stdlib recon/intel tool that maps a target's
detected tech fingerprints to endoflife.date product slugs and
reports the lifecycle status of each release: supported, security-only, EOL-soon,
extended/ESM support, or past-EOL. Past-EOL software is a factual, verifiable finding
(unsupported = no security patches), so this complements CVE lookup during recon.

$ python3 tools/eol_check.py --tech "php=7.4,ubuntu=20.04,php=8.2,nextjs" --target example.com
  [CONFIRMED]     php 7.4 (cycle 7.4)     :: EXPIRED — EOL 1322d ago
  [POSSIBLE]      ubuntu 20.04 (cycle 20.04) :: EXTENDED — EOL 407d ago  [extended/ESM may apply — verify entitlement]
  [INFORMATIONAL] php 8.2 (cycle 8.2)     :: SECURITY_ONLY  [active support ended; security-maintenance only]
  [INFORMATIONAL] nextjs                  :: UNKNOWN  [no version supplied]

Why it fits the repo

  • Pure stdlib (urllib/json/ssl) — nothing to install, so it can never be the
    "missing dependency" that skips a scan. Drops into tools/ alongside the other Python tools.
  • No traffic to the target — endoflife.date is a public metadata API.
  • Uses the repo's confidence tags: [CONFIRMED] / [POSSIBLE] / [INFORMATIONAL].
  • Offline-cached (24h, ~/.cache/bughunter/eol), so repeat runs are free.

Design principle: prefer "indeterminate" over guessing

A lifecycle checker that guesses is worse than useless in a security context. So:

  • An unmatched or absent version yields unknown — the tool never classifies an
    install from an unrelated release, and never emits [CONFIRMED] it can't substantiate.
  • Standard EOL is distinguished from extended/ESM support (reads support + eol +
    extendedSupport), so an ESM-covered release (e.g. Ubuntu 20.04 → 2030) is reported as
    [POSSIBLE] extended, not [CONFIRMED] no fixes.
  • String-based cycle matching on canonical identifiers — handles v-prefixed and
    patch-level versions and refuses ambiguous / mixed-identifier matches (e.g. won't match
    an AWS-Lambda nodejs18.x runtime against a ruby4.0 cycle).
  • Product slugs are validated against /api/all.json; genuinely ambiguous fingerprints
    (iis, bare powershell) are omitted rather than mapped to a wrong product.

Safety / robustness (hardened against malformed / hostile data)

  • Rejects control / format / separator characters by Unicode category (Cc/Cf/Cs/Zl/Zp)
    in any identifier, so a malformed cycle id or version can't be str.strip()-canonicalized
    into a false match; requires canonical YYYY-MM-DD dates so a non-schema date can't
    substantiate a verdict.
  • Validates API and cache payload types (degrades to unknown instead of crashing on a
    non-list / rate-limit / truncated body), only matches string cycle ids, treats duplicate
    or ambiguous cycles as indeterminate, and downgrades a past-EOL record whose extended/ESM
    support can't be ruled out to [POSSIBLE] rather than [CONFIRMED].
  • Sanitizes slugs (no path traversal, no raw slug in the URL), caps both response and cache
    size, dedups fetches per run, and fails soft on every I/O / parse / cache-write path.
  • NO_COLOR + non-TTY aware; strips terminal control characters from rendered output and
    from the --json artifact.
  • CLI exit codes for automation: 2 = a [CONFIRMED] past-EOL item is present;
    3 = --strict and something was indeterminate (lets CI tell "checked, fine" from
    "couldn't check"); 1 = a requested --json artifact could not be written; else 0.

Tests

tests/test_eol_check.py58 deterministic tests (network mocked; all dates built
relative to "today" so nothing rots). Covers the no-fabrication contract, version matching
edge cases (v-prefix, patch-level, boundary, mixed/duplicate identifiers, Unicode-separator
rejection), extended/security-support semantics, malformed-payload and crash robustness,
slug + output sanitization, dedup, and CLI exit codes.

The tool was hardened across several adversarial review passes specifically to ensure it
prefers "indeterminate" over ever emitting an unsubstantiated [CONFIRMED] finding.

Notes

  • Targets the stable endoflife.date REST endpoint. A future enhancement could move to
    /api/v1/products/{product} to consume its native isEoas/isMaintained fields.
  • Lifecycle data is courtesy of endoflife.date (MIT licensed); the credit string is
    retained in both the tool output and the JSON export.

…date

Pure-stdlib recon/intel tool: maps detected tech fingerprints to endoflife.date
product slugs and reports whether each release is supported, security-only,
nearing EOL, in extended/ESM support, or past EOL. Complements CVE lookup with a
factual, verifiable finding (unsupported software = no security patches).

Design principle: prefer 'indeterminate' over guessing. An unmatched, absent, or
malformed version yields 'unknown' — the tool never classifies an install from an
unrelated release and never emits [CONFIRMED] it cannot substantiate.

- Confidence tags: past-EOL -> [CONFIRMED]; EOL-soon / extended-support ->
  [POSSIBLE]; supported / security-only / unknown -> [INFORMATIONAL].
- String-based cycle matching on canonical identifiers (handles v-prefixed and
  patch-level versions; refuses ambiguous / duplicate / mixed-identifier matches).
- Distinguishes standard EOL from extended/ESM support (reads support + eol +
  extendedSupport) so ESM-covered releases aren't overstated as 'no fixes'.
- Slugs validated against /api/all.json; ambiguous fingerprints (iis, bare
  powershell, generic java/openjdk) omitted rather than mapped to a wrong product;
  vendor-specific JDKs (temurin/corretto/zulu/oracle) resolve precisely.
- Hardened against malformed/hostile data: rejects control/format/separator chars
  (by Unicode category) in identifiers, requires canonical dates, validates
  payload types, bounds response + cache size, sanitizes slugs (no path
  traversal) and output, fails soft on every I/O / parse / cache path.
- CLI exit codes: 2 = a CONFIRMED past-EOL item; 3 = --strict + indeterminate;
  1 = requested --json artifact could not be written.
- 58 deterministic tests (network mocked; dates relative so nothing rots).

Credit: lifecycle data from endoflife.date (MIT).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant