Dashboard

What does an attack surface coverage dashboard show in Metabase?

An attack surface coverage dashboard shows whether your scanners and sensors actually see everything: scan coverage by asset group, EDR deployment, assets nobody has scanned in 30 days, and the unknowns discovered this week. It's built from inventory and scan data synced from tools such as Tenable, CrowdStrike, Wiz, and Orca Security.

For: security engineers, infrastructure teams, and CISOs. Grain: one row per asset, joined to its latest scan and sensor check-in.

What does an attack surface coverage dashboard look like?

Here's the layout this guide builds, at the grain of a single asset: how much of the inventory is actually scanned and sensored, which asset classes and teams the gaps sit in, and how fast new assets are appearing. Read it before you trust any vulnerability count, because coverage sets that count's denominator.

Attack surface coverage dashboard in Metabase showing scan and EDR coverage, uncovered assets, and discovery trends.
An example attack surface coverage dashboard in Metabase, built from Tenable, CrowdStrike, and Wiz data. Figures are illustrative.

Which cards belong on an attack surface coverage dashboard?

  • Scan coverage by asset group (bar)
  • EDR sensor deployment coverage by environment (bar)
  • Assets not scanned in 30 days with owner (table)
  • New assets discovered per week by source (line)
  • Unmanaged / unknown assets — seen by discovery, absent from inventory (table)
  • Coverage trend over time by environment (line)

What data does the dashboard need?

  • assets — the authoritative inventory, with source, first_seen, environment, and status.
  • scan_events — asset, scanner, and last_scanned_at.
  • sensor_inventory — EDR sensor install and last check-in per host.

How do you build it?

  1. Build the inventory by reconciling independent sources — cloud APIs, the CMDB, and scanner discovery — so the denominator isn't just what the scanner already sees.
  2. Join each asset to its most recent scan and sensor check-in, keeping scan and agent coverage as separate facts.
  3. Classify never-seen-recently assets as stale or decommissioned using liveness signals, so dead machines leave the denominator.
  4. Build the coverage-by-group bar and unscanned-assets table first; add discovery trends and unmanaged-asset views next.

Example card SQL

Scan coverage by asset group, trailing 30 daysPostgreSQL
SELECT
  a.asset_group,
  COUNT(*) AS total_assets,
  COUNT(*) FILTER (
    WHERE s.last_scanned_at >= CURRENT_DATE - INTERVAL '30 days'
  ) AS scanned_last_30d,
  ROUND(
    100.0 * COUNT(*) FILTER (
      WHERE s.last_scanned_at >= CURRENT_DATE - INTERVAL '30 days'
    ) / NULLIF(COUNT(*), 0), 1
  ) AS coverage_pct
FROM assets a
LEFT JOIN (
  SELECT asset_id, MAX(last_scanned_at) AS last_scanned_at
  FROM scan_events
  GROUP BY asset_id
) s ON s.asset_id = a.asset_id
WHERE a.status = 'active'
GROUP BY a.asset_group
ORDER BY coverage_pct ASC;

Metrics

Integrations

Dashboards

FAQ

What is an attack surface coverage dashboard?
It answers the question every scanner dashboard silently assumes: is everything actually being scanned and protected? It compares the authoritative asset inventory against what Tenable and Wiz scanned recently and where CrowdStrike or SentinelOne sensors are deployed. Your open critical vulnerabilities count is only meaningful multiplied by scan coverage — zero findings on an unscanned host is a blind spot, not a clean bill.
What's the denominator problem in coverage metrics?
Coverage is scanned assets ÷ total assets, and the denominator is the hard part: your coverage number is only as good as your inventory. If the inventory only contains what the scanner discovered, coverage is 100% by construction and means nothing. Build assets by reconciling independent sources — cloud provider APIs, the CMDB, and scanner discovery — and treat assets that appear in only one source as the interesting rows. The unmanaged-assets card exists precisely to surface what the scanners alone would never admit is missing.
How do you handle agent-based vs. agentless coverage?
Track them as separate cards, because they fail differently. Agent coverage (EDR sensors) is per-host binary — the sensor is installed and checking in, or it isn't — and gaps come from broken deploy pipelines or unsupported OSes. Agentless coverage from Orca Security or Wiz is account-scoped: connect the cloud account and everything in it is visible, so gaps mean whole unconnected accounts. One blended percentage hides which failure mode you have; two cards tell you whether to fix a pipeline or connect an account.
How do you tell stale assets from decommissioned ones?
An asset unscanned for 30 days is either a coverage gap or a machine that no longer exists — and the dashboard must not conflate them. Cross-check against liveness signals: still present in the cloud provider API, still checking into the EDR, still emitting network traffic through Cloudflare or your load balancers. Alive-but-unscanned goes on the gap card for the vulnerability management team to fix; gone-everywhere gets marked decommissioned so it leaves the denominator instead of rotting your coverage number.
Is 100% coverage the right target?
Not globally — for ephemeral infrastructure it's the wrong frame entirely. Autoscaled nodes and short-lived containers routinely die before a 30-day scan window closes, so chasing a global 100% makes teams either exclude ephemeral infra silently or drown in false gaps. Set targets by environment: near-100% for long-lived production servers, pipeline-level guarantees (image scanning at build time) for ephemeral fleets, and a looser bar for dev. The coverage-by-environment card in this security analytics view makes those different bars explicit instead of hiding them in one blended number.