Jira: TC-5744 | PR: guacsec/trustify#2553 | Date: 2026-08-26
PR #2553 adds integration with CISA's Known Exploited Vulnerabilities (KEV) catalog to Trustify. The KEV catalog is a curated list of ~1,300 CVEs that are actively exploited in the wild, maintained under Binding Operational Directive 22-01. It complements existing data in Trustify:
- CSAF/VEX (already in Trustify): "Is this product affected by this CVE?"
- CVSS (already in Trustify): "How severe is this CVE?"
- KEV (this PR): "Is this CVE being exploited in the wild right now?"
The PR was submitted by Waldemar Kindler (Think Ahead Technologies), their third contribution to the project. It was AI-assisted (Claude Fable 5 co-authored). After a thorough review cycle with rh-jfuller including a full architectural rework, it was merged on 2026-08-18 with "LGTM."
Accept the contribution for a near-future RHTPA release, gated on completing the follow-up work items below. The code is well-structured, follows project conventions, has strong test coverage (91.15% patch, +0.15% project), and the architecture was explicitly shaped by reviewer feedback into a generic, extensible design. The remaining gaps are UX, documentation, and minor hardening -- not fundamental issues.
| Area | Status | Notes |
|---|---|---|
| Database migration | Done | exploit table, generic (source-qualified), content-derived UUIDv5 PKs |
| Entity model | Done | No FK to vulnerability -- ingestion-order independent |
| Ingestor (KEV loader) | Done | Format detection, graceful date parsing, empty-set guard |
| Importer (periodic sync) | Done | HTTP download, Last-Modified continuation, 64 MiB cap, timeouts |
| REST API | Done | GET /v3/exploit, GET /v3/exploit/{id}, pagination, full-text query |
| Vulnerability details | Done | exploits array on VulnerabilityDetails response |
| OpenAPI spec | Done | Schemas, paths, tags all generated |
| Tests (unit) | Done | Creator, loader, model, date serialization |
| Tests (integration) | Done | Ingest, idempotency, removal, revision, empty rejection, importer runner |
| Tests (e2e) | Done | Hurl tests: upload, retrieve, filter, cross-reference |
| Bug fix (shared) | Done | Date column type was bound as text in query framework |
| Sample data | Done | Disabled-by-default daily KEV importer preset |
Scope: 2,672 lines added, 39 files (20 new), 25 commits.
The contribution follows Trustify conventions well:
- Generic
exploitentity -- not CISA-specific;sourcecolumn supports future providers (VulnCheck, ExploitDB) - Creator pattern -- uses
ExploitCreatorwith batch add/create per CONVENTIONS.md - Full-sync semantics -- delete-and-reinsert per source, justified because CISA withdraws and revises entries; protected by empty-set guard against broken downloads
- JSONB metadata -- source-specific fields (vendor, product, vulnerability name, required action, ransomware use, CWEs) stored in metadata; only universal fields (dates, CVE ID) are promoted columns
- Content-derived IDs -- UUIDv5 from
(source, cve_id)ensures stable API URLs across resyncs - No FK to vulnerability -- same pattern as
vulnerability.cwes; entries join at query time oncve_id - Observability -- instrumented per conventions (not reviewed in detail)
Deviations from standard patterns (all justified in PR discussion):
- Full-sync instead of
ON CONFLICT DO NOTHING-- necessary because CISA revises and withdraws entries remediation_due_dateas a real column instead of metadata -- needed for efficient date filtering/sorting of BOD deadlines
-
UI integration (TC-5754) -- The feature is API-only. The frontend needs to surface exploit/KEV data. At minimum:
- Show an "Exploited" indicator on vulnerability details pages
- Display KEV metadata (date added, remediation deadline, ransomware use)
- Decision point: Does this need UXD review/mockups, or can engineering implement a reasonable default?
-
Documentation (TC-5755) -- Only an importer README was added. User-facing documentation is needed:
- How to enable the KEV importer
- What the exploit data means and how to interpret it
- How exploit data relates to existing advisory/vulnerability data
-
Dedicated exploit permissions (guacsec/trustify#2596) -- The exploit endpoints currently reuse advisory permissions. Exploits are a distinct entity and need their own set of permissions.
-
Vulnerability list filtering (TC-5756) -- Users should be able to filter the vulnerability list to show only CVEs with known exploits. This is the highest-value UX feature -- it answers "which of my vulnerabilities are actively exploited?"
-
SBOM-level exploit summary (TC-5757) -- Surface how many CVEs in a given SBOM are on the KEV list. This connects exploit intelligence to the user's actual software inventory.
-
QE test assets and performance testing (TC-5758) -- The contribution includes good automated tests but uses a 3-entry fixture catalog. QE needs:
- The full CISA KEV catalog (~1,300 entries) as a test dataset to validate real-world behavior end-to-end
- Performance tests covering ingestion (full-sync cycle: download, parse, delete+reinsert) and query paths (list, filter, vulnerability details with exploits) against the full dataset
- Manual test scenarios and acceptance criteria for the UI integration
-
Scalability if the KEV catalog grows -- The implementation assumes a small catalog (~1,300 entries today). The catalog's growth rate is a CISA policy decision and could accelerate. If it grows significantly, the following design choices would need revisiting:
- Full-sync in a single transaction --
ExploitCreator::createperforms an unbatchedDELETE FROM exploit WHERE source = ?followed by chunked inserts, all in one transaction. At large row counts this holds locks for an extended period and blocks concurrent API reads. - Entire dataset held in memory -- The walker downloads the full catalog into a
Vec<u8>(capped at 64 MiB), the loader deserializes all entries at once, and the creator stores them in aBTreeMapthen collects into aVec<ActiveModel>-- so the full dataset is in memory multiple times simultaneously. - No delta/incremental sync -- Every sync cycle replaces all entries for the source, even if only one entry changed.
Not a blocker today, but worth monitoring.
- Full-sync in a single transaction --
-
Delta-based sync for the KEV importer (TC-5759) -- The CISA KEV API offers no delta feed, and unlike the git-based importers (which cache previous state and diff), the KEV importer does a full delete+reinsert every cycle. Caching the previous catalog locally and computing a delta client-side would enable targeted upserts and deletes instead of a full replacement.
| Risk | Severity | Mitigation |
|---|---|---|
| Full-sync atomicity -- delete+reinsert ~1,300 rows runs inside a transaction; concurrent API reads during sync could see stale/empty data briefly | Low | Catalog is small; transaction is fast; Last-Modified means syncs are infrequent (daily, skipped if unchanged) |
| AI-assisted code -- Most commits co-authored by Claude Fable 5 | Low | Code was thoroughly reviewed by rh-jfuller with 8+ inline comments; full architectural rework was done; 91% test coverage provides safety net |
| Maintenance burden -- External contributor; ongoing maintenance falls on the team | Medium | Code follows project conventions closely; architecture was reviewer-directed; Think Ahead is a repeat contributor (3rd PR) |
No upload endpoint for exploit catalogs -- KEV catalog requires explicit ?format=cisakev on the advisory upload endpoint; not auto-detected because Format::Advisory only matches CSAF/CVE/OSV |
Low | Importer bypasses this entirely; tracked as guacsec/trustify#2605 / TC-5753 |
| No cascade delete -- Deleting a vulnerability does not delete its exploit entries | Low | By design (no FK); exploit entries are source-managed via full-sync, not vulnerability-managed |
| Upstream catalog changes -- CISA could change the JSON schema | Low | Only catalogVersion field is required for detection; all entry fields except cve_id are optional with graceful degradation |
Air-gapped installations (TC-5760) -- The importer downloads the catalog from cisa.gov over HTTPS; air-gapped or network-restricted deployments cannot reach the upstream source |
Medium | Not solved yet. Possible approaches: manual upload via a future POST /api/v3/exploit endpoint (TC-5753), a local mirror, or a configurable source URL pointing to an internal host. Needs consideration before GA. |
- Release timing: Accept for the next RHTPA release (with UI work), or defer to a later release?
- UXD involvement: Is a UXD review needed before building the UI, or can engineering proceed with a reasonable default?
- Scope of UI work: Minimal (indicator + details panel) vs. comprehensive (filtering, SBOM summary, dashboard integration)?
- Test assets: Does QE need to create dedicated test plans, or are the automated tests sufficient for initial release?