"""Windows version / PowerShell capability probe. Used by privacy features to log a single clear reason when a function silently no-ops on older Windows Server hosts (Server 2008 R2 ships PowerShell 2.0 which does not have ``Get-NetAdapter`` / ``Set-NetAdapter`` / ``Disable-NetAdapterBinding`` / ``Get-DnsClientServerAddress`` / ``Rename-Computer``). """ from __future__ import annotations import logging import platform import subprocess import sys from dataclasses import dataclass from functools import lru_cache log = logging.getLogger(__name__) @dataclass(frozen=True) class WinCompat: release: str # e.g. "10", "2012ServerR2" build: int # major build number powershell_major: int # 0 = PowerShell not detected has_net_cmdlets: bool # Get-NetAdapter etc. (PS 3.0+ on Server 2012+) has_defender: bool # Add-MpPreference cmdlet exists def summary(self) -> str: if sys.platform != "win32": return ( f"{platform.system()} release={self.release} " "Windows-only privacy controls disabled" ) return ( f"Windows release={self.release} build={self.build} " f"PowerShell={self.powershell_major}.x " f"NetAdapter cmdlets={'yes' if self.has_net_cmdlets else 'no'} " f"Defender={'yes' if self.has_defender else 'no'}" ) def _powershell_major() -> int: try: r = subprocess.run( ["powershell", "-NoProfile", "-NonInteractive", "-Command", "$PSVersionTable.PSVersion.Major"], capture_output=True, text=True, timeout=12, creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), ) out = (r.stdout or "").strip().splitlines()[-1] if r.stdout else "" return int(out) if out.isdigit() else 0 except Exception: return 0 def _has_cmdlet(name: str) -> bool: try: r = subprocess.run( ["powershell", "-NoProfile", "-NonInteractive", "-Command", f"if (Get-Command {name} -ErrorAction SilentlyContinue) " f"{{ 'yes' }} else {{ 'no' }}"], capture_output=True, text=True, timeout=10, creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), ) return "yes" in (r.stdout or "").lower() except Exception: return False @lru_cache(maxsize=1) def probe() -> WinCompat: """Cached host probe. Cheap on subsequent calls.""" if sys.platform != "win32": info = WinCompat( release=platform.release(), build=0, powershell_major=0, has_net_cmdlets=False, has_defender=False, ) log.info("Compat probe: %s", info.summary()) return info release = platform.release() try: build = int(platform.version().split(".")[-1]) except Exception: build = 0 ps = _powershell_major() # PowerShell >= 3.0 is the gate for the modern Net* cmdlets that all the # privacy features rely on. Anything older (Server 2008 R2 RTM) only has # PS 2.0 and needs WMI / netsh / ipconfig fallbacks. has_net = ps >= 3 and _has_cmdlet("Get-NetAdapter") has_def = _has_cmdlet("Add-MpPreference") info = WinCompat( release=release, build=build, powershell_major=ps, has_net_cmdlets=has_net, has_defender=has_def, ) log.info("WinCompat probe: %s", info.summary()) return info