#!/usr/bin/env python3
"""Scope CVE-2026-56155 exposure from local inventories and exported logs."""
from __future__ import annotations
import json, os, sys
from pathlib import Path
CVE_IDS=['CVE-2026-56155']
VENDOR='Microsoft'
PRODUCT='Active Directory Federation Services (AD FS)'
AFFECTED_VERSION_SELECTORS=['windows_server_2012_r2_server_core_installation < 6.3.9600.23291', 'windows_server_2012_r2 < 6.3.9600.23291', 'windows_server_2012_server_core_installation < 6.2.9200.26226', 'windows_server_2012 < 6.2.9200.26226', 'windows_server_2016_server_core_installation < 10.0.14393.9339', 'windows_server_2016 < 10.0.14393.9339', 'windows_10_version_1607_for_x64_based_systems < 10.0.14393.9339', 'windows_10_version_1607_for_32_bit_systems < 10.0.14393.9339', 'windows_server_2025 < 10.0.26100.33158', 'windows_server_2025_server_core_installation < 10.0.26100.33158', 'windows_server_2022 < 10.0.20348.5386', 'windows_server_2019_server_core_installation < 10.0.17763.9020', 'windows_server_2019 < 10.0.17763.9020', 'windows_10_version_1809_for_x64_based_systems < 10.0.17763.9020', 'windows_10_version_1809_for_32_bit_systems < 10.0.17763.9020']
SOURCE_SELECTORS=['https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json', 'https://nvd.nist.gov/vuln/detail/CVE-2026-56155', 'https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-56155', 'https://api.msrc.microsoft.com/sug/v2.0/en-US/affectedProduct?$filter=cveNumber%20eq%20%27CVE-2026-56155%27', 'https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-56155']
DOMAINS=[]; HASHES=[]; URLS=[]; FILES=[]; NETWORK_PATTERNS=[]
PROCESS_PATTERNS=['Active Directory Federation Services (AD FS)', 'Microsoft', 'CVE-2026-56155']
PRODUCT_TELEMETRY_PATTERNS=['AD FS', 'administrator privileges', 'Security Update', 'Monthly Rollup']
INDICATORS=list(dict.fromkeys(CVE_IDS+[VENDOR,PRODUCT]+AFFECTED_VERSION_SELECTORS+SOURCE_SELECTORS+PRODUCT_TELEMETRY_PATTERNS))
OUT=os.environ.get('OUT','hp-microsoft-adfs-cve-2026-56155-kev-scope')
TEXT_SUFFIXES={'.csv','.json','.jsonl','.txt','.log','.yaml','.yml','.xml','.conf','.ini','.md'}
EXCLUDED_DIR_NAMES={'.git','node_modules','vendor','dist','__pycache__','.venv'}
def read_text(path: Path)->str:
try: return path.read_text(encoding='utf-8', errors='ignore')
except Exception: return ''
def iter_files(root: Path):
if root.is_file(): yield root; return
for p in root.rglob('*'):
if p.is_file() and not any(part in EXCLUDED_DIR_NAMES for part in p.parts):
if not p.suffix or p.suffix.lower() in TEXT_SUFFIXES: yield p
def scan(root: Path):
matches=[]; lowered=[s.lower() for s in INDICATORS if s]
for path in iter_files(root):
low=read_text(path).lower(); hits=sorted({INDICATORS[i] for i,s in enumerate(lowered) if s in low})
if hits: matches.append({'path':str(path),'hits':hits})
return matches
def main(argv=None):
argv=argv or sys.argv[1:]; root=Path(argv[0]) if argv else Path('.')
matches=scan(root); out_dir=Path(OUT); out_dir.mkdir(parents=True, exist_ok=True)
report={'cves':CVE_IDS,'vendor':VENDOR,'product':PRODUCT,'affected_version_selectors':AFFECTED_VERSION_SELECTORS,'match_count':len(matches),'matches':matches}
(out_dir/'scope_report.json').write_text(json.dumps(report,indent=2),encoding='utf-8')
print(json.dumps({'cves':CVE_IDS,'match_count':len(matches),'report':str(out_dir/'scope_report.json')},indent=2))
return 2 if matches else 0
if __name__=='__main__': raise SystemExit(main())