What is vulnerability SLA compliance, and how do you measure it in Metabase?
Vulnerability SLA compliance is the share of findings remediated within their severity-based SLA window — criticals in days, lows in months. It's the metric that turns remediation from a best effort into a commitment, and the one auditors and customers ask for by name. Measure it in Metabase from findings synced from Tenable, Wiz, or Snyk joined to your SLA policy.
closed within SLA ÷ all findings due in period, by severity. The denominator is everything whose deadline fell in the window — including findings still open past due. Leave those out and the number lies.What vulnerability SLA compliance measures
It measures whether the remediation process keeps its promises. Where mean time to remediate describes typical speed, SLA compliance is pass/fail against a stated policy — which is why it shows up in customer security reviews and audit evidence requests. It comes with a built-in work queue: every open finding past its due date is a named breach with an owner and an age.
What data does it need?
- A
vulnerability_findingstable withfirst_seen_at,resolved_at,status, and normalizedseverity. - An SLA policy as data: a
severity → sla_daysmapping table the SQL joins against. - Asset metadata for grouping breaches by owning team (and for tighter SLAs on internet-facing assets, if your policy has them).
- Stable finding identity across scans, so a finding's due date doesn't reset every time it's re-observed.
SQL patterns
WITH sla AS (
SELECT * FROM (VALUES
('critical', 7),
('high', 30),
('medium', 90),
('low', 180)
) AS t (severity, sla_days)
)
SELECT
f.severity,
COUNT(*) AS findings_due,
COUNT(*) FILTER (
WHERE f.resolved_at IS NOT NULL
AND f.resolved_at <= f.first_seen_at + s.sla_days * INTERVAL '1 day'
) AS closed_in_sla,
ROUND(
100.0 * COUNT(*) FILTER (
WHERE f.resolved_at IS NOT NULL
AND f.resolved_at <= f.first_seen_at + s.sla_days * INTERVAL '1 day'
) / NULLIF(COUNT(*), 0), 1
) AS sla_compliance_pct
FROM vulnerability_findings f
JOIN sla s ON s.severity = f.severity
-- everything whose SLA deadline fell in the last 90 days,
-- closed or not: open-and-overdue counts against you
WHERE f.first_seen_at + s.sla_days * INTERVAL '1 day'
>= CURRENT_DATE - INTERVAL '90 days'
AND f.first_seen_at + s.sla_days * INTERVAL '1 day' < CURRENT_DATE
GROUP BY f.severity
ORDER BY sla_compliance_pct;WITH sla AS (
SELECT * FROM (VALUES
('critical', 7),
('high', 30),
('medium', 90),
('low', 180)
) AS t (severity, sla_days)
)
SELECT
f.finding_id,
f.severity,
a.asset_group,
f.first_seen_at::date AS first_seen,
(f.first_seen_at + s.sla_days * INTERVAL '1 day')::date AS sla_due,
CURRENT_DATE
- (f.first_seen_at + s.sla_days * INTERVAL '1 day')::date AS days_overdue
FROM vulnerability_findings f
JOIN sla s ON s.severity = f.severity
JOIN assets a ON a.id = f.asset_id
WHERE f.status = 'open'
AND f.first_seen_at + s.sla_days * INTERVAL '1 day' < now()
ORDER BY days_overdue DESC;Pitfalls
first_seen_at — when a scanner first saw the finding — full stop.Where this metric applies
- Tenable + Metabase — host findings with first-seen and fixed timestamps
- Wiz + Metabase — cloud issues with detection and resolution times
- Snyk + Metabase — dependency findings from introduction to fix
Related
Metrics
Dashboards
FAQ
What SLA windows should we use?
severity → sla_days mapping table) so the SQL and the policy document can never drift apart.Do open findings count against compliance?
When does the SLA clock start?
first_seen_at — when a scanner first observed the finding — not when a ticket was created or when your pipeline imported the row. Ticket-creation clocks reward slow triage; import clocks reward slow syncs. Tenable and Wiz both expose first-seen timestamps, and your mean time to remediate should run on the same clock so the two metrics reconcile.How do you calculate vulnerability SLA compliance?
closed_in_sla / findings_due. Join findings to an SLA mapping table on severity, compute each finding's due date as first_seen_at + sla_days, and segment the result by severity — a 95% overall figure can hide a 60% critical figure.