Fix CISA access and publish scanner recovery/status events

This commit is contained in:
2026-10-04 14:40:08 +02:00
parent af7d9e6612
commit 792dd928b1
+35 -15
View File
@@ -20,6 +20,29 @@ CONFIG = Path('/etc/wazuh-docker-security/config.json')
LOG = Path('/var/log/wazuh-docker-security/events.jsonl')
SOCKET = 'unix:///var/run/docker.sock'
KEV_URL = 'https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json'
KEV_URLS = (KEV_URL, 'https://raw.githubusercontent.com/cisagov/kev-data/main/known_exploited_vulnerabilities.json')
def fetch_kev(url, now):
"""Only publisher-controlled sources; reject invalid/old mirror snapshots."""
request = urllib.request.Request(url, headers={'User-Agent': 'wazuh-docker-security/1'})
with urllib.request.urlopen(request, timeout=15) as response:
raw = response.read(12 * 1024 * 1024 + 1)
if len(raw) > 12 * 1024 * 1024:
raise ValueError('KEV response too large')
document = json.loads(raw)
published = dt.datetime.fromisoformat(document['dateReleased'].replace('Z', '+00:00')).timestamp()
if not -86400 <= now - published <= 14 * 86400:
raise ValueError('KEV publication timestamp outside freshness window')
entries = document['vulnerabilities']
if document.get('count') != len(entries):
raise ValueError('incomplete KEV catalog')
ids = sorted({v['cveID'] for v in entries
if re.fullmatch(r'CVE-\d{4}-\d{4,}', v.get('cveID', ''))})
if not ids or len(ids) != len(entries):
raise ValueError('empty or invalid KEV catalog')
return {'fetched_at': now, 'published_at': published, 'ids': ids,
'catalog_version': document.get('catalogVersion'), 'source': url}
def atomic_json(path, data):
@@ -88,21 +111,16 @@ def kev_catalog():
now = time.time()
error = None
if now - cached.get('fetched_at', 0) >= 86400:
try:
request = urllib.request.Request(KEV_URL, headers={'User-Agent': 'wazuh-docker-security/1'})
with urllib.request.urlopen(request, timeout=30) as response:
raw = response.read(12 * 1024 * 1024 + 1)
if len(raw) > 12 * 1024 * 1024:
raise ValueError('KEV response too large')
document = json.loads(raw)
ids = sorted({v['cveID'] for v in document['vulnerabilities']
if re.fullmatch(r'CVE-\d{4}-\d{4,}', v.get('cveID', ''))})
if not ids:
raise ValueError('empty KEV catalog')
cached = {'fetched_at': now, 'ids': ids}
atomic_json(path, cached)
except Exception:
error = 'KEV refresh failed; cached information may be incomplete'
for url in KEV_URLS:
try:
updated = fetch_kev(url, now)
atomic_json(path, updated)
cached = updated
break
except Exception:
continue
else:
error = 'KEV refresh failed on both official sources; cached information may be incomplete'
fresh = now - cached.get('fetched_at', 0) <= 7 * 86400
if not fresh:
error = 'KEV catalog unavailable or older than seven days'
@@ -237,6 +255,8 @@ def main():
for key in state['health']:
if key not in errors:
events.append({'event': 'scan_recovered', 'component': key})
events.append({'event': 'scan_status', 'complete': 'yes' if inventory_ok and not any(k.startswith('image:') for k in errors) else 'no',
'kev_fresh': 'yes' if fresh else 'no', 'problem_count': len(errors)})
append_events(events)
atomic_json(state_path, {'findings': merged, 'health': errors, 'last_scan': time.time()})
atomic_json(BASE / 'inventory.json', {'updated_at': time.time(), 'scope': scope,