Files
vulncheck/tests/test_eol_dotnet.py
T
vulncheck 0b71453c48 chore(release): drop personal attribution from comments and docstrings
Comments across the codebase credited one individual by role and, in places,
described that person's own machines: which SQL Server versions a host ran,
which devices were enrolled, what a particular dashboard showed, how many
findings sat open on which server. In a public repository that reads as a
profile of someone's unpatched estate.

The observations are why the code looks the way it does, so they stay. Every
CVE id, version, build number, count and date is preserved, as are the
verbatim quotes that motivated specific sort and filter rules — only the
attribution changes, to "field report", "observed", "a host". A local
variable in tests/test_autodesk_year.py was renamed for the same reason; its
value and every assertion around it are byte-identical.

PROJECT_OVERVIEW.md additionally loses a subtitle naming the kind of
organisation this was built for, and a support section pointing at an
internal team, both replaced with neutral wording.

Comments, docstrings and markdown prose only: 74 files, 200 lines, one-for-one
swaps. detect_changes reports 104 touched symbols and zero affected execution
flows, and all 55 test scripts pass. Nothing here needs re-testing.
2026-08-26 15:34:05 +02:00

122 lines
5.7 KiB
Python

""".NET / .NET Framework EOL detection — run: python tests/test_eol_dotnet.py
Two defects, one product family:
1. ".NET" normalises to "net" (the dot is stripped), so the curated "dotnet" /
"dotnetframework" slug keys could never match a real inventory entry. Every
.NET and .NET Framework install — the dead ones included — was skipped.
2. Modern .NET reports an MSI build in the version field ("Microsoft .NET Host
- 9.0.18 (x64)" → 72.72.55158). Even with the slug fixed, no release
prefix-matches that, so the check still returned nothing. The release comes
from the display NAME.
Plus the source rule: for these two products endoflife.date decides alone —
Microsoft's listing must never produce an EOL finding for them.
"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from app.services import eol_service as E
from app.services import ms_lifecycle_service as M
# A real Wazuh inventory, verbatim.
INVENTORY = [
("Microsoft .NET 8.0.29 - Windows Server Hosting", "8.0.29.26325", "8"),
("Microsoft .NET Host - 10.0.10 (x64)", "80.40.55332", "10"),
("Microsoft .NET Host - 8.0.29 (x64)", "64.116.55299", "8"),
("Microsoft .NET Host - 8.0.29 (x86)", "64.116.55299", "8"),
("Microsoft .NET Host - 9.0.18 (x64)", "72.72.55158", "9"),
("Microsoft .NET Host FX Resolver - 10.0.10 (x64)", "80.40.55332", "10"),
("Microsoft .NET Host FX Resolver - 8.0.16 (x86)", "64.64.32758", "8"),
("Microsoft .NET Runtime - 9.0.18 (x64)", "72.72.55158", "9"),
("Microsoft ASP.NET Core 8.0.11 - Shared Framework (x64)", "8.0.11.24572", "8"),
("Microsoft Windows Desktop Runtime - 6.0.36 (x64)", "64.36.30422", "6"),
]
# endoflife.date payload shape, pinned so the test needs no network and no
# clock: dates are what /api/v1/products/dotnet and /dotnetfx serve.
CATALOG = {
"dotnet": {"result": {"releases": [
{"name": "10", "eolFrom": "2028-11-14", "isMaintained": True},
{"name": "9", "eolFrom": "2026-11-10", "isMaintained": True},
{"name": "8", "eolFrom": "2026-11-10", "isMaintained": True},
{"name": "7", "eolFrom": "2024-05-14", "isMaintained": False},
{"name": "6", "eolFrom": "2024-11-12", "isMaintained": False},
{"name": "3.1", "eolFrom": "2022-12-13", "isMaintained": False},
]}},
"dotnetfx": {"result": {"releases": [
{"name": "4.8.1", "eolFrom": None, "isMaintained": True},
{"name": "4.8", "eolFrom": None, "isMaintained": True},
{"name": "4.7.2", "eolFrom": None, "isMaintained": True},
{"name": "4.6.2", "eolFrom": "2027-01-12", "isMaintained": True},
{"name": "4.6.1", "eolFrom": "2022-04-26", "isMaintained": False},
{"name": "4.5.2", "eolFrom": "2022-04-26", "isMaintained": False},
]}},
}
def demo():
E.fetch_product = lambda db, slug: CATALOG.get(slug)
# 1) every inventory entry maps to the right product and the right release.
for name, version, want_release in INVENTORY:
assert E.resolve_product_slug(name) == "dotnet", name
st = E.check_eol(None, name, version)
assert st is not None, name
assert st.release_name == want_release, f"{name}: {st.release_name} != {want_release}"
# 2) supported vs dead, decided by endoflife.date alone. .NET 9 is NOT
# end-of-life (Nov 2026) — the whole point of the report.
def status(name, version):
return E.check_eol(None, name, version)
for name, version in (("Microsoft .NET Host - 9.0.18 (x64)", "72.72.55158"),
("Microsoft .NET Host - 8.0.29 (x64)", "64.116.55299"),
("Microsoft .NET Host - 10.0.10 (x64)", "80.40.55332")):
st = status(name, version)
assert not st.is_eol, f"{name} must not be end-of-life"
dead = status("Microsoft Windows Desktop Runtime - 6.0.36 (x64)", "64.36.30422")
assert dead.is_eol and dead.eol_date == "2024-11-12"
# 3) .NET Framework — name and version field agree, the release wins either way.
assert E.resolve_product_slug("Microsoft .NET Framework 4.8") == "dotnetfx"
fx48 = E.check_eol(None, "Microsoft .NET Framework 4.8", "4.8.04084")
assert fx48.release_name == "4.8" and not fx48.is_eol
fx461 = E.check_eol(None, "Microsoft .NET Framework 4.6.1", "4.6.01055")
assert fx461.is_eol and fx461.eol_date == "2022-04-26"
# 4) developer packs are build inputs, not an installed runtime.
for pack in ("Microsoft .NET Framework 4.5.2 Multi-Targeting Pack",
"Microsoft .NET Framework 4.6.1 Targeting Pack",
"Microsoft .NET Framework 4.5.2 Developer Pack"):
assert E.dotnet_slug(pack) is None, pack
assert E.resolve_product_slug(pack) is None, pack
# 5) neighbours that merely contain the letters must not be swallowed.
for other in ("Microsoft Network Monitor 3.4",
"Microsoft Visual Studio .NET 2003",
"Microsoft .NET Micro Framework Porting Kit 3.0"):
assert E.dotnet_slug(other) is None, other
# 6) Microsoft's lifecycle listing gets no vote on .NET — endoflife.date only.
# (It would fire without the guard: the export is consulted for every
# name the slug map misses.)
M.fetch_lifecycle_data = lambda db, force_refresh=False: [
{"name": "Microsoft .NET Host", "release": "Original Release",
"end_date": "2020-01-01"},
{"name": ".NET Framework 4.8", "release": "Original Release",
"end_date": "2020-01-01"},
]
for name, version, _ in INVENTORY:
assert M.resolve_ms_lifecycle_eol(None, name, version) is None, name
assert M.resolve_ms_lifecycle_eol(None, "Microsoft .NET Framework 4.8", "4.8.04084") is None
print("eol .NET self-check OK")
if __name__ == "__main__":
demo()