fix(teams): skip the 1.0.0.0 placeholder, keep Teams Panel/Phone off Teams

Field case: an Intune host lists "MSTeams 1.0.0.0" (CN=Microsoft
Corporation). It drew CVE-2023-4863, -29328, -29330, CVE-2022-21965 and
CVE-2025-53783, plus the Teams classic EOL.

1.0.0.0 names no build. It is the new client's MSIX package (Intune's
"MSTeams", Wazuh's WindowsApps\MSTeams_1.0.0.0_...) with no version read;
no desktop Teams shipped as 1.0.0.0 and classic was never MSIX. Four parts
under 100, so it passed the scheme check and sat below every classic and
Android bound. teams_placeholder_version (1.0.0.0, 1.0, 1, 0.0.0.0) is now
skipped by the cvelistV5 and NVD scans and by _teams_classic. 0a73eafb had
read it as classic; "MSTeams" now counts as the new client for EOL, like
"Microsoft Teams". Real classic builds (1.5.00.31168) keep both.

CVE-2025-53783 came through teams_panels: the NVD filter matched the
vendor:product token as a substring, so microsoft:teams also took
teams_panels and teams_phones (1.0.9x device builds), and Teams for
Android (1.0.0.2025...) got their CVE and the Panel fix 1.0.97.2025102203.
Teams now takes its CPE exactly. Other products keep the prefix match;
Adobe relies on it (acrobat_dc). NVD cache prefix v5.
This commit is contained in:
2026-10-08 13:15:00 +02:00
parent 6ce578fcda
commit 81bafda04a
8 changed files with 91 additions and 11 deletions
+12
View File
@@ -1440,6 +1440,18 @@ entry, else the first listed. Windows hosts were told the macOS build
is skipped: "Teams for Mac" is not paired, so it held only the desktop's
ranges; NVD's macOS entries cover the Mac.
Version 1.0.0.0 (also 1.0, 1, 0.0.0.0) is a placeholder, not a build:
Intune's "MSTeams 1.0.0.0" and Wazuh's WindowsApps\MSTeams_1.0.0.0_… are the
new client's MSIX package with no build read. No desktop Teams shipped as
1.0.0.0, and classic was never MSIX. Read as a build it sat below every
classic and Android bound (CVE-2023-29328/-29330/-4863, CVE-2022-21965) and
drew the classic EOL. Both CVE paths and `_teams_classic` skip it
(`teams_placeholder_version`); "MSTeams" otherwise counts as the new client
for EOL, like "Microsoft Teams". On NVD the Teams lookup takes
`microsoft:teams` exactly: `teams_panels` and `teams_phones` (1.0.9x builds)
gave Teams for Android their CVE-2025-53783 fix. Prefix matching stays for
everything else, Adobe relies on it (`acrobat_dc`).
**Oracle Java SE, JRE and JDK** (key `oracle-java` on cvelistV5; NVD rows
`oracle:jdk` and `oracle:jre`, each with its `sun:` twin for the Sun-era
records, 6-8 only; a JDK also asks for the JRE's CVEs). Version from the
+25 -1
View File
@@ -1092,6 +1092,21 @@ def _same_version_scheme(installed: str, bound: str) -> bool:
return (a[0] >= 100) == (b[0] >= 100)
def teams_placeholder_version(version: str) -> bool:
"""A Teams version that names no build: 1.0.0.0, 1.0, 1, 0.0.0.0.
Intune lists the new client's MSIX package as "MSTeams 1.0.0.0", and
Wazuh reads the same from WindowsApps\\MSTeams_1.0.0.0_…. No Teams desktop
client ever shipped as 1.0.0.0 (classic is 1.N.00.NNNNN, the machine-wide
installer 1.N.0.NNNNN, new Teams 2xxxx.x.x.x). Read as a build, it sits
below every classic and Android bound of the same shape: CVE-2023-29328,
-29330, -4863, CVE-2022-21965 and the Teams classic EOL all landed on it.
The CVE scanners and the EOL check skip it; the version is unknown, not
old.
"""
return bool(re.fullmatch(r"[01](?:\.0+)*", (version or "").strip()))
def dotnet_sdk_version(v) -> bool:
"""A modern .NET version whose patch is 100+ is an SDK, not a runtime.
@@ -1274,6 +1289,12 @@ def _query_nvd_cpe(cpe: str, version: str, sap_pl: Optional[int] = None,
if prod_token not in crit and not (
loadmaster and ":progress:multi-tenant_" in crit):
continue # Multi Tenant: its 7.1.35 line, see loadmaster_fix
if prod_token == "microsoft:teams" and ":microsoft:teams:" not in crit:
# teams_panels / teams_phones: own devices, own 1.0.9x
# builds. Teams for Android (1.0.0.2025…) sat below
# them and took their CVEs and fix (CVE-2025-53783).
# Teams only: Adobe needs the prefix (acrobat_dc).
continue
if not m.get("vulnerable", True):
continue
if skip_editions and (crit.split(":") + [""] * 10)[9].lower() \
@@ -1412,9 +1433,10 @@ def _cache_put(db: Session, product_key: str, version: str, cves: List[dict]) ->
db.commit()
# v5: Teams Panel / Phone CPEs no longer answer for Teams.
# v4: Teams bounds scheme-checked, fixes kept per platform. Also drops answers
# cached before every NVD page was read.
CACHE_PREFIX = "v4:"
CACHE_PREFIX = "v5:"
def lookup_cves(db: Session, entry: dict, version: str,
@@ -1823,6 +1845,8 @@ def scan_asset_packages(db: Session, asset, packages: list, new_ids: Optional[li
entry = resolve_product(name)
if not entry:
continue
if entry["key"] == "cpe:microsoft:teams" and teams_placeholder_version(version):
continue
java_upd = None
if entry.get("java"):
# Asked as the release (1.6.0) and decided by the update; shown
+2
View File
@@ -3817,6 +3817,8 @@ def scan_asset(db: Session, asset, packages: list, index: dict,
# at 1.6.00.17554 (its own fix) sat below the desktop's 18681.
# NVD carries the macOS entries.
continue
if key == "teams" and cpe.teams_placeholder_version(version):
continue # "MSTeams 1.0.0.0" names no build
strict = key in _STRICT_SCHEME_KEYS
for entry in index[key]:
if not _affected(cver, entry.get("start"), entry.get("lt"),
+9 -2
View File
@@ -34,6 +34,7 @@ import httpx
from sqlalchemy.orm import Session
from app.models.setting import Setting
from app.services.app_cve_scanner_service import teams_placeholder_version
from app.services.eol_service import EOLStatus, EOL_SOON_DAYS, _days_until
logger = logging.getLogger(__name__)
@@ -130,6 +131,10 @@ def _teams_classic(name: str, version: str) -> bool:
every shipping new-Teams build is 2xxxx.x.x.x, never 1.x.
Intune detectedApps lists the same package by its AppX name "MSTeams".
1.0.0.0 is none of them: it is what Intune and Wazuh read off the new
client's MSIX package (WindowsApps\\MSTeams_1.0.0.0_…) when they get no
build. Classic was never an MSIX package; see teams_placeholder_version.
The version test must not widen past an exact "teams" name — "Microsoft
Teams Meeting Add-in for Microsoft Office" is a live product at 1.24.x.
"""
@@ -139,8 +144,10 @@ def _teams_classic(name: str, version: str) -> bool:
if "teams classic" in low or "classic teams" in low:
return True
ver = (version or "").strip()
if teams_placeholder_version(ver):
return False
if _normalise(name) in ("teams", "msteams"):
return ver == "1" or ver.startswith("1.")
return ver.startswith("1.")
return "machine-wide installer" in low and ver.startswith("1.")
@@ -405,7 +412,7 @@ def resolve_ms_lifecycle_eol(
# plain "Microsoft Teams", which is exactly the export's listing name for
# BOTH releases — the day Microsoft fills a retirement date on the classic
# row, a current Teams would read as CRITICAL EOL.
if _normalise(pname) == "teams":
if _normalise(pname) in ("teams", "msteams"):
return None
# 1) hardcoded exotics (Silverlight, old VC++ redists)
+13 -5
View File
@@ -9,12 +9,16 @@ nothing at all. Fixed date instead, matched on name+version.
Three inventory entries are that product:
Teams Machine-Wide Installer 1.5.0.31168
Microsoft Teams classic 1.8.00.18356
Microsoft Teams 1.0.0.0
Microsoft Teams 1.8.00.18356
The third is what Wazuh syscollector reports on a host where the client was
never renamed — same name the CURRENT client uses (26198.304.4946.9672), and
so does a machine-wide installer belonging to it. Hence the version, not the
name, decides: every shipping new-Teams build is 2xxxx.x.x.x, never 1.x.
1.0.0.0 is not classic. It is what Intune ("MSTeams") and Wazuh
(WindowsApps\\MSTeams_1.0.0.0_…) read off the new client's MSIX package when
they get no build; no Teams desktop client shipped as 1.0.0.0.
The version test is pinned to an exact "Microsoft Teams" name, because
"Microsoft Teams Meeting Add-in for Microsoft Office" is a live product
sitting at 1.24.x and must stay clean.
@@ -45,11 +49,7 @@ def test_classic_is_eol():
for name, version in (
("Microsoft Teams classic", "1.8.00.18356"),
("Teams Machine-Wide Installer", "1.5.0.31168"),
# Wazuh syscollector, WindowsApps MSTeams_1.0.0.0_x64__8wekyb3d8bbwe
("Microsoft Teams", "1.0.0.0"),
("Microsoft Teams", "1.8.00.18356"),
# Intune detectedApps lists the same package by its AppX name
("MSTeams", "1.0.0.0"),
):
st = _resolve(name, version)
assert st is not None, f"{name}: no EOL finding"
@@ -65,6 +65,13 @@ def test_current_teams_is_not_eol():
assert _resolve("Teams Machine-Wide Installer", "26198.304.4946.9672") is None
def test_placeholder_version_is_not_classic():
"""Intune: "MSTeams 1.0.0.0"; Wazuh: WindowsApps\\MSTeams_1.0.0.0_…."""
for name in ("MSTeams", "Microsoft Teams", "Teams", "Teams Machine-Wide Installer"):
for version in ("1.0.0.0", "1.0", "1"):
assert _resolve(name, version) is None, (name, version)
def test_meeting_addin_is_not_teams():
"""Live product at 1.24.x — the 1.x rule must not reach it."""
assert _resolve(
@@ -75,5 +82,6 @@ def test_meeting_addin_is_not_teams():
if __name__ == "__main__":
test_classic_is_eol()
test_current_teams_is_not_eol()
test_placeholder_version_is_not_classic()
test_meeting_addin_is_not_teams()
print("OK — Teams classic EOL 2024-07-01, current Teams + add-in clean")
+28 -1
View File
@@ -69,7 +69,13 @@ NVD = {"vulnerabilities": [
_m("*", "1.6.00.26474", edition="desktop")]),
("CVE-2025-53783", [_m("android", "1.0.0.2025102802"), _m("iphone_os", "7.10.1"),
_m("macos", "25122.1207.3700.1444"),
_m("*", "25122.1415.3698.6812", edition="desktop")]),
_m("*", "25122.1415.3698.6812", edition="desktop"),
{"vulnerable": True, "versionEndExcluding": "1.0.97.2025102203",
"criteria": "cpe:2.3:a:microsoft:teams_panels:*:*:*:*:*:*:*:*"},
{"vulnerable": True, "versionEndExcluding": "1.0.94.2025168802",
"criteria": "cpe:2.3:a:microsoft:teams_phones:*:*:*:*:*:*:*:*"}]),
# No platform on the bounded entry: Android / Rooms numbering.
("CVE-2022-21965", [_m("*", "1.0.94.20000")]),
)]}
@@ -208,6 +214,27 @@ def demo():
assert mac["CVE-2023-4863"] == "1.6.00.26463", mac
assert mac["CVE-2023-29328"] == "1.6.00.17554", mac
# --- placeholder ------------------------------------------------------------
# Intune: "MSTeams 1.0.0.0" — the new client's MSIX package without a
# build. Four parts and below every classic / Android bound, so it drew
# CVE-2023-29328/-29330 (cvelistV5 + NVD), -4863, CVE-2022-21965 and,
# via teams_panels' 1.0.97.x, CVE-2025-53783.
for version in ("1.0.0.0", "1.0.0", "1", "0.0.0.0"):
for name in ("MSTeams", "Microsoft Teams", "Teams"):
assert _scan5(index, name, version) == [], (name, version)
assert _scan_nvd("Windows 11 Enterprise", version) == [], version
# …while a real classic build still gets them, and the old bound's
# Android numbering (1.0.94.x) is not a placeholder.
assert _scan_nvd("Windows 11 Enterprise", "1.5.00.31168") == _nvd("1.5.00.31168")
assert not cpe.teams_placeholder_version("1.0.94.19999")
# --- Teams Panel / Phone --------------------------------------------------
# Their own CPEs (teams_panels, teams_phones), not Teams. Teams for
# Android (1.0.0.2025…) sits below their 1.0.9x bounds and picked them up,
# fix included. It gets the Android entry only.
c = next(c for c in _query("1.0.0.2025102801") if c["cve"] == "CVE-2025-53783")
assert c["fixes"] == {"android": "1.0.0.2025102802"}, c
print("ok classic Teams 1.5.00.31168 gets CVE-2023-29328/-29330 from both paths "
"with the desktop fix, CVE-2023-24881 (2.x only) stays off it")
+1 -1
View File
@@ -37,7 +37,7 @@ def test_other_families_and_arp_entries_untouched():
_msix("Microsoft Teams", "MSTeams", "26225.1806.5074.1452"),
_msix("Company Portal", "Microsoft.CompanyPortal", "11.2.1.0"),
# Same name, classic ARP install, no WindowsApps folder — a different
# product line and must survive (EOL scan flags it).
# product line and must survive.
{"name": "Microsoft Teams", "version": "1.0.0.0", "location": ""},
{"name": "Mozilla Firefox", "version": "141.0"},
]
+1 -1
View File
@@ -117,7 +117,7 @@ def _index():
return _INDEX_SETTING
@check("NVD result cache prefix", "v4:")
@check("NVD result cache prefix", "v5:")
def _cache():
from app.services.app_cve_scanner_service import CACHE_PREFIX
return CACHE_PREFIX