feat(vuln-id): per-vulnerability KEV/EPSS enrichment columns on the Vulnerability entity - #15426
Merged
Merged
Conversation
…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
…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
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
blakeaowens
approved these changes
Jul 30, 2026
devGregA
approved these changes
Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[sc-14060]
Description
The
Vulnerabilityentity landed ondev(#15331) withepss_score/epss_percentile/known_exploited/ransomware_used/kev_dateand a docstring promising "NULL until enrichmentpopulates 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_dateDefectDojo 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_updatedWithout 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 = NULLcannot be told apartfrom a genuine negative, which matters because the consumer projects these values onto findings:
Finding.known_exploitedisBooleanField(default=False), so a projection driven off anun-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_tablesrather than stacked as a new migration.0281–0288aredev-only (masterandbugfixboth stop at0280), so nothing has shippedwith 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 --checkis 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_exploitedthese columns enable — are implemented and tested.🤖 Generated with Claude Code
https://claude.ai/code/session_01UuXapymdUZ2XaTgc9HRjSA