Summary
As a KAT user, I want a report that shows when each finding was first detected and when it was resolved, so I can track the lifecycle of vulnerabilities over time and demonstrate remediation progress to stakeholders.
Currently there is no way to get a timeline view of findings. The existing reports show a point-in-time snapshot, but not when findings appeared or disappeared.
Use cases
- Compliance reporting: Show auditors that findings are being addressed within SLA timeframes
- Trend analysis: Track whether the overall security posture is improving or degrading over time
- Remediation tracking: Identify findings that have been open the longest and need attention
- Change correlation: Correlate finding appearances/resolutions with infrastructure changes
Current state
- Octopoes already has
get_entity_history() (octopoes/octopoes/xtdb/client.py) which calls XTDB's entity-history API. This returns transaction records per entity, including valid-time ranges.
FindingsReport and VulnerabilityReport already call get_history() per finding, but only to determine first-seen date — and they do it as N+1 individual calls, which is a known performance bottleneck.
- XTDB v1 (>= 1.22.1) supports
get-start-valid-time and get-end-valid-time Datalog predicates, which would allow fetching timeline data for all findings in a single query instead of N+1 calls.
Proposed approach
Phase 1: Timeline report with existing API
- New report type in Rocky:
FindingTimelineReport
- Uses existing
get_entity_history() to collect first-seen and last-seen dates per finding
- Output: table with columns: Finding | Severity | First seen | Resolved | Duration open
- Accept this will be slow for large orgs due to N+1 history calls
Phase 2: Optimize with XTDB temporal predicates
- Upgrade XTDB to >= 1.22.1 (current latest v1 is 1.24.4)
- Add a new Octopoes endpoint (e.g.
GET /{client}/findings/timeline) that uses get-start-valid-time / get-end-valid-time in a single Datalog query
- This replaces the N+1 entity-history calls with one bulk query
- Update the report to use the new endpoint
Phase 3: Dashboard integration
- Add a timeline widget to the Crisis Room dashboard
- Show trend chart: open findings over time
- Highlight newly appeared and recently resolved findings
Technical notes
- Current XTDB version: Custom image
docker.underdark.nl/librekat/xtdb-http-multinode:main — exact version unknown, needs verification
- Latest XTDB v1: 1.24.4 (June 2024) — v1 is in maintenance mode, v2 is the active development line
- XTDB v2 migration: Out of scope for this issue, but worth noting that v2 (latest: 2.1.0) has native SQL support and richer temporal queries. A future migration would further improve this feature.
Related
Summary
As a KAT user, I want a report that shows when each finding was first detected and when it was resolved, so I can track the lifecycle of vulnerabilities over time and demonstrate remediation progress to stakeholders.
Currently there is no way to get a timeline view of findings. The existing reports show a point-in-time snapshot, but not when findings appeared or disappeared.
Use cases
Current state
get_entity_history()(octopoes/octopoes/xtdb/client.py) which calls XTDB's entity-history API. This returns transaction records per entity, including valid-time ranges.FindingsReportandVulnerabilityReportalready callget_history()per finding, but only to determine first-seen date — and they do it as N+1 individual calls, which is a known performance bottleneck.get-start-valid-timeandget-end-valid-timeDatalog predicates, which would allow fetching timeline data for all findings in a single query instead of N+1 calls.Proposed approach
Phase 1: Timeline report with existing API
FindingTimelineReportget_entity_history()to collect first-seen and last-seen dates per findingPhase 2: Optimize with XTDB temporal predicates
GET /{client}/findings/timeline) that usesget-start-valid-time/get-end-valid-timein a single Datalog queryPhase 3: Dashboard integration
Technical notes
docker.underdark.nl/librekat/xtdb-http-multinode:main— exact version unknown, needs verificationRelated
get_history()calls are a contributing factor)