Files
vulncheck/alembic/versions/047_kev_alert_sources_add_vulncheck.py
T
vulncheck fa5351a703 fix(kev): the alert mail was still a two-catalog feature
VulnCheck KEV shipped as a third exploited-catalog source in f77c741 and the
dashboard reads it, but the immediate-alert mail never did: db_init seeded
kev_alert_sources as "cisa,euvd" on the reasoning that a ~4x larger catalog
should be an opt-in, and there is no UI to opt in with. An exploited CVE that
only VulnCheck listed sat open on an active asset and produced no mail.

Catalog size is not what gates a mail — an OPEN finding on an ACTIVE asset is.
Whichever catalog names it, an exploited CVE on our own machine is worth the
alert, and VulnCheck usually lists days before CISA (lead_days is computed
from exactly that gap), which is the window an immediate alert exists for.

Seed is now all three. Migration 047 rewrites the value on installs still
carrying the untouched old default; anything an operator changed by hand is
left alone — a default correction, not a policy override.

The plumbing was already there: get_kev_catalog / _normalize_sources /
the {{sources}} label line all handle vulncheck. Only the seed was in the way.
Two stale docstrings naming just CISA and ENISA fixed with it.

First run after upgrade sends a backlog, not a storm: MAX_ALERTS_PER_MAIL
caps it at 25 and the job is hourly.
2026-08-29 18:03:15 +02:00

53 lines
1.8 KiB
Python

"""Add VulnCheck KEV to the immediate-alert sources
Revision ID: 047
Revises: 046
Create Date: 2026-08-29 12:00:00.000000
VulnCheck KEV shipped as a third exploited-catalog source (f77c741) and the
dashboard reads it, but the alert mail did not: `kev_alert_sources` was seeded
"cisa,euvd" on the reasoning that a ~4x larger catalog should be an opt-in.
That reasoning does not survive contact with what the alert actually gates on
— an OPEN finding on an ACTIVE asset. Catalog size does not decide a mail; our
own inventory does. And VulnCheck typically lists a CVE days before CISA (we
already compute `lead_days` from it), which is exactly the window an immediate
alert exists for.
The seed is now all three. This rewrites the value on installs that still
carry the untouched old default. A value an operator changed to anything else
is left alone — this is a default correction, not a policy override.
Note for whoever reads this after the first run: the mails that follow are a
backlog, not a storm. They are capped at MAX_ALERTS_PER_MAIL (25) per run and
the job is hourly, so a large first batch drains over a few hours.
"""
from alembic import op
revision = "047"
down_revision = "046"
branch_labels = None
depends_on = None
_OLD = "cisa,euvd"
_NEW = "cisa,euvd,vulncheck"
def upgrade() -> None:
# Tolerate the spacing a hand-edited value may carry ("cisa, euvd").
op.execute(
"UPDATE settings SET value = '%s' "
"WHERE key = 'kev_alert_sources' "
"AND replace(replace(lower(value), ' ', ''), '\"', '') = '%s'"
% (_NEW, _OLD)
)
def downgrade() -> None:
op.execute(
"UPDATE settings SET value = '%s' "
"WHERE key = 'kev_alert_sources' "
"AND replace(replace(lower(value), ' ', ''), '\"', '') = '%s'"
% (_OLD, _NEW)
)