Files
vulncheck/tests/test_igel_isn.py
T
vulncheck 2921278f5b feat(igel): the vendor's own notices, for the flaws that never get a CVE
Many IGEL flaws are published only as an ISN — ISN 2026-09 (UMS information
disclosure, 8.6), ISN 2025-31 (stored XSS in UMS, 8.0), ISN 2025-24 (command
execution in IGEL OS, 8.8) carry no CVE id at all, so neither the CPE path nor
the cvelistV5 path could ever see them. 21 of the 149 ISNs with a stated fix
are like that.

igel_isn_service reads kb.igel.com the way teamviewer_bulletin_service reads
the TeamViewer bulletins: the overview links every notice, each page states
the CVSS at the top and the fixed version per release line under "Update
Instructions" ("OS 12: Update to IGEL OS 12.7.1. OS 11: … 11.10.410"). That
per-line fix is the bound; the ISN id stands in for the CVE where there is
none. App versions on an OS line ("update the Chromium app to 151.x") are not
firmware bounds and are dropped; a UMS line told to move to a newer major
affects the whole old line.

Runs for both the endpoints and the UMS server, in the IGEL sync and in the
nightly app scan, between the CPE pass and the cvelistV5 pass so the shared
touched-set keeps every reconcile honest. The UMS server never had a reconcile
at all; it has one now. The index is rebuilt with the other vendor indexes.

Verified against all 198 live notices (parser and prototype agree on every
page) and by tests/test_igel_isn.py.
2026-09-03 12:19:16 +02:00

271 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""IGEL Security Notices (ISN) — run: python tests/test_igel_isn.py
Field report (03.09.2026): many IGEL flaws never get a CVE id, only the
vendor's ISN id — ISN 2026-09 (UMS information disclosure, CVSS 8.6),
ISN 2025-31 (stored XSS in UMS, 8.0), ISN 2025-24 (command execution in IGEL
OS, 8.8). No CVE-keyed source can see them; only the vendor page states the
CVSS at the top and the fixed versions under "Update Instructions".
The HTML below is the article text of the live pages, verbatim, in the
Confluence export's heading structure (main > h1, h2 Summary / Details /
Update Instructions / References). Exercises the production parser and the
production scan decision, not copies of their logic.
"""
import json
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from app.services import igel_isn_service as isn
from app.services import app_cve_scanner_service as cpe
from app.services import cvelistv5_scan_service as c5
def page(title, head, summary, details, update, refs=""):
return (f"<html><body><nav>Breadcrumbs Home Security &amp; Safety</nav><main>"
f"<h1>{title}</h1><p>{head}</p>"
f"<h2>Summary</h2><p>{summary}</p>"
f"<h2>Details</h2><p>{details}</p>"
f"<h2>Update Instructions</h2><p>{update}</p>"
+ (f"<h2>References</h2><p>{refs}</p>" if refs else "")
+ "</main><script>// Mark block images on the first level as breakout"
"</script></body></html>")
# ---- verbatim from kb.igel.com (fetched 03.09.2026) ------------------------
UMS_2026_09 = page(
"ISN 2026-09: UMS Information Disclosure",
"First published 1 April 2026 CVSS:3.1: 8.6 (High) CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L",
"A security vulnerability has been found in the UMS. This affects the following "
"product versions: IGEL UMS in version 12.10.100 and older",
"It has been discovered that an HTTPS endpoint in UMS disclosed multiple files "
"without authentication.",
"Update to UMS 12.11.100 or newer when available.")
UMS_2025_31 = page(
"ISN 2025-31: XSS Vulnerabilities in UMS",
"First published 28 July 2025 CVSS:3.1: 8.0 (High) CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H",
"Multiple instances of Stored Cross-Site Scripting (XSS) vulnerabilities found that "
"affect the following products: IGEL Universal Management Suite versions <=12.08.110",
"After internal and external security testing, multiple instances of stored "
"Cross-Site Scripting (XSS) vulnerabilities have been found in IGEL UMS.",
"UMS: Update to version 12.08.130")
UMS_2023_27 = page(
"ISN 2023-27: ActiveMQ in UMS HA",
"First published 3 November 2023 CVSS 3.1: 10.0 (Critical) CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:H/A:H",
"Apache ActiveMQ is vulnerable to a critical remote code execution vulnerability. "
"This vulnerability affects the High Availability (HA) feature only , used in UMS "
"in the following versions: UMS versions <= 12.02.120",
"Apache ActiveMQ is vulnerable to a critical (10.0) remote code execution "
"vulnerability being tracked with CVE-2023-46604.",
"UMS 12: We are preparing an emergency release of UMS 12.02.130. "
"UMS 6: Upgrade to UMS 12.02.130, available soon.",
"CVE-2023-46604: https://nvd.nist.gov/vuln/detail/CVE-2023-46604")
OS_2026_18 = page(
"ISN 2026-18: “Copy fail” Kernel Vulnerability",
"First published 8 May 2026 CVSS:3.1: 7.8 (High) CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"An important security vulnerability has been found in the Linux Kernel used in "
"IGEL OS. This affects the following product versions: IGEL OS 12 IGEL OS 11",
"A local privilege escalation has been discovered in the Linux Kernel. This is "
"tracked as CVE-2026-31431 and rated as high.",
"OS 12: Upgrade the Base System app to version 12.8.2 LTS or 12.9.0 as soon as "
"they are available. OS 11: Upgrade to IGEL OS 11.11.150 as soon as it is available.",
"CVE-2026-31431 https://www.cve.org/CVERecord?id=CVE-2026-31431")
OS_2026_08 = page(
"ISN 2026-08: Telnetd Vulnerabilities",
"First published 26 March 2026 CVSS:3.1: 7.8 (High) CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"A security vulnerability has been found in Telnetd, a network service used in IGEL "
"OS for the Secure Terminal feature. This affects the following product versions: IGEL OS 12",
"Telnetd in GNU Inetutils through 2.7 allows a remote authentication bypass "
"(CVE-2026-24061). In addition, there is an out-of-bounds write (CVE-2026-32746). "
"OS 11 is not affected, as it uses a different Telnetd implementation.",
"OS 12: Update to the base system app in version 12.7.6 or newer when available "
"from the IGEL App Portal.",
"CVE-2026-24061: https://nvd.nist.gov/vuln/detail/CVE-2026-24061 "
"CVE-2026-32746: https://nvd.nist.gov/vuln/detail/CVE-2026-32746")
OS_2025_24 = page(
"ISN 2025-24: Command Execution in IGEL OS",
"First published 30 June 2025 CVSS:3.1: 8.8 (High) CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
"A security vulnerability has been found in the IGEL OS base system. This affects "
"the following product versions: IGEL OS 12 IGEL OS 11",
"An issue has been found in the way IGEL OS handles the LD_PRELOAD environment variable.",
"OS 12: Update to IGEL OS 12.7.1. OS 11: Update to IGEL OS 11.10.410 when available "
"(planned for July).")
# An "Updated" line BEFORE "First published" carries the fix version — it must
# not be read as the score.
OS_2026_07 = page(
"ISN 2026-07: AppArmor Vulnerabilities",
"Updated 30 March 2026 (updated OS 12 fix version to 12.8.1) First published 26 March 2026 "
"CVSS:3.1: 7.8 (High) CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"IGEL OS 12 IGEL OS 11",
"Two AppArmor flaws, CVE-2026-1234 and CVE-2026-1235.",
"OS 12: Update to IGEL OS 12.8.1. OS 11: Update to IGEL OS 11.11.150.")
# Chromium ISN: the OS 12 fix is an APP version — no firmware bound. OS 11 is.
OS_CHROMIUM = page(
"ISN 2026-34: Critical Chromium Vulnerabilities",
"First published 1 September 2026 CVSS:3.1: 9.6 (Critical)",
"IGEL OS 12 IGEL OS 11",
"CVE-2026-9000.",
"OS 12: Update the Chromium app to version 151.0.7922.137 or newer. "
"OS 11: Update to IGEL OS 11.11.160.")
# No fix version at all → nothing (cannot become a per-device verdict).
OS_PREPARING = page(
"ISN 2024-05: Something",
"First published 1 February 2024 CVSS:3.1: 8.4 (High)",
"IGEL OS 12", "CVE-2024-1.", "OS 12: IGEL is preparing a fixed OS 12 base system version.")
def parse(slug, html):
e = isn._parse_isn(slug, html)
assert e, slug
return e
def bounds(e):
return [(b["prod"], b["release"], b["fix"], b["all"]) for b in e["affected"]]
def test_parser():
e = parse("isn-2026-09-ums-information-disclosure", UMS_2026_09)
assert (e["id"], e["cvss"], e["sev"], e["cves"]) == ("ISN-2026-09", 8.6, "high", [])
assert bounds(e) == [("ums", 12, "12.11.100", False)]
assert e["url"].endswith("/isn-2026-09-ums-information-disclosure")
assert e["desc"].startswith("A security vulnerability has been found in the UMS.")
e = parse("isn-2025-31-xss-vulnerabilities-in-ums", UMS_2025_31)
assert (e["cvss"], e["sev"], e["cves"]) == (8.0, "high", [])
assert bounds(e) == [("ums", 12, "12.08.130", False)]
e = parse("isn-2023-27-activemq-in-ums-ha", UMS_2023_27)
assert (e["cvss"], e["sev"], e["cves"]) == (10.0, "critical", ["CVE-2023-46604"])
assert bounds(e) == [("ums", 12, "12.02.130", False), ("ums", 6, "12.02.130", True)]
e = parse("isn-2026-18-copy-fail-kernel-vulnerability", OS_2026_18)
assert (e["cvss"], e["sev"], e["cves"]) == (7.8, "high", ["CVE-2026-31431"])
assert bounds(e) == [("os", 12, "12.8.2", False), ("os", 11, "11.11.150", False)]
e = parse("isn-2026-08-telnetd-vulnerabilities", OS_2026_08)
assert e["cves"] == ["CVE-2026-24061", "CVE-2026-32746"]
assert bounds(e) == [("os", 12, "12.7.6", False)] # OS 11 not affected
e = parse("isn-2025-24-command-execution-in-igel-os", OS_2025_24)
assert (e["cvss"], e["cves"]) == (8.8, [])
assert bounds(e) == [("os", 12, "12.7.1", False), ("os", 11, "11.10.410", False)]
e = parse("isn-2026-07-apparmor-vulnerabilities", OS_2026_07)
assert e["cvss"] == 7.8, e["cvss"]
assert bounds(e) == [("os", 12, "12.8.1", False), ("os", 11, "11.11.150", False)]
e = parse("isn-2026-34-critical-chromium-vulnerabilities", OS_CHROMIUM)
assert bounds(e) == [("os", 11, "11.11.160", False)]
assert isn._parse_isn("isn-2024-05-something", OS_PREPARING) is None
print(" parser OK")
class _Asset:
def __init__(self, os_name, version):
self.id = 1
self.hostname = "tc-01"
self.operating_system = os_name
self.os_version = version
def run_scan(asset, index):
"""scan_asset with the two DB writers stubbed — records what it would write."""
written, closed = [], []
orig_upsert, orig_resolve = cpe._upsert, c5._resolve_stale_appliance
def fake_upsert(db, a, pkg, version, c, new_ids, touched=None, vendor=None):
written.append((c["cve"], pkg, c["fixed"], c["cvss"], c["severity"], json.loads(c["refs"])[0]))
if touched is not None:
touched.add(c["cve"].upper())
new_ids.append(len(new_ids) + 1)
def fake_resolve(db, a, label, still, evidence, tag):
closed.append((label, sorted(still), tag))
return 0
cpe._upsert, c5._resolve_stale_appliance = fake_upsert, fake_resolve
try:
touched = set()
n = isn.scan_asset(None, asset, [], touched=touched, index=index)
finally:
cpe._upsert, c5._resolve_stale_appliance = orig_upsert, orig_resolve
return n, written, closed, touched
def test_scan():
index = {}
for slug, html in (("isn-2026-09-ums-information-disclosure", UMS_2026_09),
("isn-2025-31-xss-vulnerabilities-in-ums", UMS_2025_31),
("isn-2023-27-activemq-in-ums-ha", UMS_2023_27),
("isn-2026-18-copy-fail-kernel-vulnerability", OS_2026_18),
("isn-2026-08-telnetd-vulnerabilities", OS_2026_08),
("isn-2025-24-command-execution-in-igel-os", OS_2025_24),
("isn-2026-34-critical-chromium-vulnerabilities", OS_CHROMIUM)):
index[slug] = parse(slug, html)
# UMS 12.08.110 — the field report's server: XSS (no CVE) + info disclosure
# (no CVE); ActiveMQ fixed in 12.02.130, so not that one.
n, written, closed, _ = run_scan(_Asset("IGEL Universal Management Suite", "12.08.110"), index)
ids = sorted(w[0] for w in written)
assert ids == ["ISN-2025-31", "ISN-2026-09"], ids
assert all(w[1] == "IGEL Universal Management Suite" for w in written)
fixes = {w[0]: w[2] for w in written}
assert fixes == {"ISN-2025-31": "12.08.130", "ISN-2026-09": "12.11.100"}, fixes
assert {w[0]: w[3] for w in written} == {"ISN-2025-31": 8.0, "ISN-2026-09": 8.6}
assert written[0][5].startswith("https://kb.igel.com/")
# The UMS server reconciles here (nothing else ever closed its findings).
assert closed == [("IGEL Universal Management Suite", ["ISN-2025-31", "ISN-2026-09"], "igel-isn")], closed
# UMS 12.11.100 — fixed everything.
n, written, closed, _ = run_scan(_Asset("IGEL Universal Management Suite", "12.11.100"), index)
assert written == [], written
assert closed[0][1] == []
# UMS 6.10.130 — whole line affected by the ActiveMQ ISN (upgrade to 12).
n, written, _, _ = run_scan(_Asset("IGEL Universal Management Suite", "6.10.130"), index)
assert [(w[0], w[2]) for w in written] == [("CVE-2023-46604", "12.02.130")], written
# OS 12.7.0+3 (build suffix as UMS reports it): copy-fail, telnetd ×2,
# LD_PRELOAD (no CVE, fixed 12.7.1). Chromium ISN: OS 12 line has no
# firmware bound.
n, written, closed, touched = run_scan(_Asset("IGEL OS", "12.7.0+3"), index)
ids = sorted(w[0] for w in written)
assert ids == ["CVE-2026-24061", "CVE-2026-31431", "CVE-2026-32746", "ISN-2025-24"], ids
assert all(w[1] == "IGEL OS" for w in written)
assert {w[0]: w[2] for w in written}["CVE-2026-31431"] == "12.8.2"
assert touched == set(ids) # scan_asset_igel's reconcile folds these in
assert closed == [], closed # …so the endpoint does NOT reconcile here
# OS 12.8.2 — copy-fail fixed, the rest fixed earlier.
n, written, _, _ = run_scan(_Asset("IGEL OS", "12.8.2"), index)
assert written == [], written
# OS 11.11.140: copy-fail (11.11.150) yes; telnetd (OS 11 not affected)
# no; LD_PRELOAD (11.10.410) no; Chromium (11.11.160) yes. Never the
# OS 12 bounds — 11 is a different product.
n, written, _, _ = run_scan(_Asset("IGEL OS", "11.11.140"), index)
got = sorted((w[0], w[2]) for w in written)
assert got == [("CVE-2026-31431", "11.11.150"), ("CVE-2026-9000", "11.11.160")], got
# Not an IGEL asset / no version → nothing, no reconcile.
assert run_scan(_Asset("Windows 11", "10.0.26100"), index)[1] == []
assert run_scan(_Asset("IGEL OS", ""), index)[2] == []
print(" scan OK")
if __name__ == "__main__":
test_parser()
test_scan()
print("test_igel_isn OK")