#!/usr/bin/env python3
"""Scope CVE-2026-15409/CVE-2026-15410 exposure from local inventories and exported logs."""
from __future__ import annotations
import json, os, sys
from pathlib import Path
CVE_IDS=['CVE-2026-15409', 'CVE-2026-15410']
VENDOR='SonicWall'
PRODUCT='SMA1000 Series Appliances'
AFFECTED_VERSION_SELECTORS=['sonicwall-sma1000@12.4.3-03245', 'sonicwall-sma1000@12.4.3-03387', 'sonicwall-sma1000@12.4.3-03434 platform-hotfix', 'sonicwall-sma1000@12.5.0-02283', 'sonicwall-sma1000@12.5.0-02624', 'sonicwall-sma1000@12.5.0-02800 platform-hotfix', 'fixed: sonicwall-sma1000 >= 12.4.3-03453 platform-hotfix', 'fixed: sonicwall-sma1000 >= 12.5.0-02835 platform-hotfix']
SOURCE_SELECTORS=['https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json', 'https://psirt.global.sonicwall.com/vuln-detail/SNWLID-2026-0008', 'https://psirtapi.global.sonicwall.com/api/v1/vulndetail/?advisory_id=SNWLID-2026-0008', 'https://nvd.nist.gov/vuln/detail/CVE-2026-15409', 'https://nvd.nist.gov/vuln/detail/CVE-2026-15410', 'https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-15409', 'https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-15410']
DOMAINS=[]; HASHES=[]; URLS=[]; FILES=[]; NETWORK_PATTERNS=[]
PROCESS_PATTERNS=['SMA1000 Series Appliances', 'SonicWall', 'CVE-2026-15409', 'CVE-2026-15410']
PRODUCT_TELEMETRY_PATTERNS=['extraweb_access.log', '/__api__/login', '/__api__/logout', '/wsproxy', 'ctrl-service.log', 'hotfix rollbacks', '/var/lib/unit/conf.json', 'platform-hotfix']
INDICATORS=list(dict.fromkeys(CVE_IDS+[VENDOR,PRODUCT]+AFFECTED_VERSION_SELECTORS+SOURCE_SELECTORS+PRODUCT_TELEMETRY_PATTERNS))
OUT=os.environ.get('OUT','hp-sonicwall-sma1000-cve-2026-15409-cve-2026-15410-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())