Skip to content

feat(vuln-id): per-vulnerability KEV/EPSS enrichment columns on the Vulnerability entity - #15426

Merged
Maffooch merged 3 commits into
devfrom
feat/vuln-entity-enrichment-columns
Jul 30, 2026
Merged

feat(vuln-id): per-vulnerability KEV/EPSS enrichment columns on the Vulnerability entity#15426
Maffooch merged 3 commits into
devfrom
feat/vuln-entity-enrichment-columns

Conversation

@Maffooch

@Maffooch Maffooch commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

[sc-14060]

Description

The Vulnerability entity landed on dev (#15331) with epss_score / epss_percentile /
known_exploited / ransomware_used / kev_date and a docstring promising "NULL until enrichment
populates them"
. Nothing populates them yet, and two columns were missing for a consumer to
actually use the store as the per-vulnerability enrichment cache the docstring describes.

kev_due_date

DefectDojo Pro tracks the CISA-assigned KEV due date and uses it to cap SLA deadlines. There was
nowhere on the entity to cache it per vulnerability, so it could not participate in a
per-vulnerability rollup.

enrichment_updated

Without a "when did we last write these columns" marker, "this id has no KEV signal" and "nobody
has looked at this id yet" are indistinguishable.
known_exploited = NULL cannot be told apart
from a genuine negative, which matters because the consumer projects these values onto findings:
Finding.known_exploited is BooleanField(default=False), so a projection driven off an
un-enriched registry would clear the KEV flag on every finding in the instance.

Indexed, since the staleness sweep orders by it.

Migration note

Amended into 0287_vulnerability_id_entity_tables rather than stacked as a new migration.
02810288 are dev-only (master and bugfix both stop at 0280), so nothing has shipped
with the current table definition — the table is now simply created with these two columns instead
of being created and then altered one migration later.

manage.py makemigrations --check is clean against the amended migration.

Scope

Schema only. No behavior change on its own: both columns are nullable with no default, and no OSS
code reads or writes them. The writer is the companion DefectDojo Pro PR (a two-stage KEV/EPSS
enrichment pipeline), which is where the per-vulnerability semantics — including the tri-state
known_exploited these columns enable — are implemented and tested.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UuXapymdUZ2XaTgc9HRjSA

…ulnerability entity

The entity landed with epss_score/epss_percentile/known_exploited/ransomware_used/kev_date
and a docstring promising "NULL until enrichment populates them" — but nothing populated
them, and two things were missing for a consumer to actually use the store:

- kev_due_date: Pro tracks the CISA-assigned due date (EnhancedFinding.kev_due_date, which
  EnhancedSLA_Configuration.cap_by_kev_due_date reads) and there was nowhere on the entity to
  cache it per vulnerability.
- enrichment_updated: without it, "this id has no KEV signal" and "nobody has looked at this
  id yet" are indistinguishable, because known_exploited=NULL cannot be told apart from a
  genuine negative once a projection has run. Indexed, since the staleness sweep orders by it.

Amended into 0287 rather than stacked: 0281-0288 are dev-only (master/bugfix stop at 0280),
so the table is created with these columns instead of altered right after.

Companion to DefectDojo-Inc/dojo-pro (KEV/EPSS two-stage enrichment pipeline), which is the
writer for these columns.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UuXapymdUZ2XaTgc9HRjSA
@Maffooch
Maffooch requested a review from blakeaowens as a code owner July 30, 2026 02:18
@github-actions github-actions Bot added the New Migration Adding a new migration file. Take care when merging. label Jul 30, 2026
…orer columns and manual sync

Brings the EPSS / KEV page in line with how enrichment now works, and adds screenshots taken from
a running instance against the live CISA KEV and EPSS feeds.

Corrected — the page described behavior that is no longer accurate:
- Sync was described as reprocessing the ENTIRE Finding population daily. It now runs in two stages:
  refresh the per-vulnerability data, then push out only the vulnerabilities whose values actually
  moved. A day with no feed changes rewrites nothing.
- "Up to roughly a day of lag" on new imports no longer has to be waited out; there is now a manual
  sync, so the page links to it instead of presenting the delay as unavoidable.
- Enrichment data is stored once per vulnerability rather than per Finding, so the page now says so
  and points at the Vulnerability Explorer as a first-class place to read it.

Added:
- Viewing KEV/EPSS in the Vulnerability Explorer — the same five columns as the Findings table
  (EPSS Score, EPSS Percentile, Known Exploited, Ransomware Used, KEV Date), what is sortable, and
  the Total Findings drill-through.
- Telling "no data" apart from "not exploited" — a blank column means never enriched, a red x means
  checked and not listed. This distinction is easy to misread as reassurance, so it gets its own
  section with both states shown.
- Running a sync on demand — the button, the progress/ETA behavior, one-at-a-time guarding, and that
  repeating a sync is safe because an unchanged feed rewrites nothing.
- Multi-CVE semantics made explicit: the EPSS percentile always belongs to the same CVE as the
  score, and a KEV signal is never suppressed by a higher-EPSS sibling.
- The SLA note was wrong by omission — it said EPSS/KEV do not affect the SLA clock. They can, via
  Cap by KEV due date, and where several KEV CVEs are present the earliest due date wins.

Screenshots are real captures from a local instance enriched from the live feeds (CVE-2021-44228 at
EPSS 100.00%, KEV-listed, ransomware, added Dec. 10 2021). Verified with `hugo`: 640 pages build
with no errors and all five images render.

Note: this page exists twice (triage_findings/epss_kev.md and
triage_findings/finding_scoring/epss_kev.md) with identical bodies and different weights. That
duplication predates this change; both copies are updated here so they cannot drift apart, but
consolidating them is worth a follow-up.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UuXapymdUZ2XaTgc9HRjSA
@github-actions github-actions Bot added the docs label Jul 30, 2026
The EPSS / KEV page existed twice with byte-identical bodies and different weights, so it rendered
as two separate pages that had to be edited in lockstep to stay consistent.

Keeps triage_findings/finding_scoring/epss_kev.md as canonical — EPSS/KEV is a scoring topic and
that section already holds cvss_support.md — and removes triage_findings/epss_kev.md.

The old URL is preserved as a Hugo alias rather than dropped, so /triage_findings/epss_kev/ keeps
working for anyone holding a link or bookmark. Nothing in the docs linked to it internally, but an
external link should not 404.

Verified with `hugo`: builds clean, aliases 111 -> 112, the old URL emits a redirect stub with
rel="canonical" and a meta refresh to the new path, the page body now appears exactly once in the
output, and all five screenshots still render.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UuXapymdUZ2XaTgc9HRjSA
@Maffooch Maffooch added this to the 3.2.0 milestone Jul 30, 2026
@Maffooch
Maffooch merged commit 4ffa0d5 into dev Jul 30, 2026
152 checks passed
@Maffooch
Maffooch deleted the feat/vuln-entity-enrichment-columns branch July 30, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs New Migration Adding a new migration file. Take care when merging.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants