<!-- Copyright (c) 2026 defconxt. All rights reserved. -->
<!-- Licensed under AGPL-3.0 — see LICENSE file for details. -->
---
name: building-devsecops-metrics-dashboard
description: >-
  Build dashboards that track MTTD, MTTR, vulnerability backlog, scan coverage,
  and security debt using Prometheus, Grafana, and custom metric collectors.
  Drive security program improvements with data-driven decisions.
domain: cybersecurity
subdomain: devsecops
tags:
  - metrics
  - dashboard
  - grafana
  - prometheus
  - mttd
  - mttr
  - vulnerability-management
  - security-debt
  - kpis
version: "1.0"
author: defconxt
license: AGPL-3.0
metadata:
  mitre-attack: []
---

# Building DevSecOps Metrics Dashboard

## Overview

DevSecOps metrics dashboards provide visibility into security posture, vulnerability
trends, and program effectiveness. This skill covers collecting metrics from scanners,
CI/CD pipelines, and vulnerability trackers, then visualizing KPIs including MTTD
(Mean Time to Detect), MTTR (Mean Time to Remediate), scan coverage, and security debt.

Mode: `[MODE: BLUE]` — Security program measurement and continuous improvement.

## Prerequisites

| Requirement | Details |
|---|---|
| Prometheus for metric collection | Required |
| Grafana for visualization | Required |
| CI/CD pipeline with scan result exports | Required |
| Vulnerability tracking system (Jira, GitHub Issues) | Required |
| Python 3.10+ for metric collectors | Required |

## Key Concepts

### Prometheus Metrics Exporter

```python
# metrics/security_exporter.py
from prometheus_client import start_http_server, Gauge, Counter, Histogram
import time

# Vulnerability metrics
VULN_TOTAL = Gauge(
    'devsecops_vulnerabilities_total',
    'Total open vulnerabilities',
    ['severity', 'scanner']
)
VULN_BACKLOG = Gauge(
    'devsecops_vulnerability_backlog_days',
    'Average age of open vulnerabilities in days',
    ['severity']
)
SCAN_COVERAGE = Gauge(
    'devsecops_scan_coverage_percent',
    'Percentage of repositories with active scanning',
    ['scan_type']
)
MTTR_SECONDS = Histogram(
    'devsecops_mttr_seconds',
    'Mean Time to Remediate in seconds',
    ['severity'],
    buckets=[3600, 86400, 604800, 2592000, 7776000]  # 1h, 1d, 1w, 30d, 90d
)
FINDINGS_RESOLVED = Counter(
    'devsecops_findings_resolved_total',
    'Total findings resolved',
    ['severity', 'resolution']
)
PIPELINE_BLOCKS = Counter(
    'devsecops_pipeline_blocks_total',
    'Deployments blocked by security gates',
    ['gate_type']
)
```

### Grafana Dashboard JSON

```json
{
  "dashboard": {
    "title": "DevSecOps Security Metrics",
    "panels": [
      {
        "title": "Open Vulnerabilities by Severity",
        "type": "stat",
        "targets": [
          {
            "expr": "devsecops_vulnerabilities_total",
            "legendFormat": "{{severity}}"
          }
        ]
      },
      {
        "title": "MTTR by Severity (days)",
        "type": "gauge",
        "targets": [
          {
            "expr": "histogram_quantile(0.5, rate(devsecops_mttr_seconds_bucket[30d])) / 86400",
            "legendFormat": "{{severity}}"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "thresholds": {
              "steps": [
                {"color": "green", "value": 0},
                {"color": "yellow", "value": 7},
                {"color": "red", "value": 30}
              ]
            }
          }
        }
      },
      {
        "title": "Scan Coverage",
        "type": "gauge",
        "targets": [
          {
            "expr": "devsecops_scan_coverage_percent",
            "legendFormat": "{{scan_type}}"
          }
        ]
      },
      {
        "title": "Pipeline Blocks (30d)",
        "type": "stat",
        "targets": [
          {
            "expr": "increase(devsecops_pipeline_blocks_total[30d])",
            "legendFormat": "{{gate_type}}"
          }
        ]
      },
      {
        "title": "Vulnerability Trend (90d)",
        "type": "timeseries",
        "targets": [
          {
            "expr": "devsecops_vulnerabilities_total",
            "legendFormat": "{{severity}}"
          }
        ]
      },
      {
        "title": "Security Debt Score",
        "type": "gauge",
        "targets": [
          {
            "expr": "sum(devsecops_vulnerabilities_total{severity='critical'}) * 10 + sum(devsecops_vulnerabilities_total{severity='high'}) * 5 + sum(devsecops_vulnerabilities_total{severity='medium'}) * 2"
          }
        ]
      }
    ]
  }
}
```

### CI Metric Collection

```yaml
name: Collect Security Metrics
on:
  schedule:
    - cron: '0 6 * * *'
  workflow_dispatch:

jobs:
  collect:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Collect scan results
        run: |
          node scripts/agent.js --action collect \
            --source-dir . \
            --output /tmp/metrics.json
      - name: Push to Prometheus
        run: |
          node scripts/agent.js --action push \
            --metrics /tmp/metrics.json \
            --pushgateway http://prometheus-pushgw:9091
```

### KPI Definitions

```yaml
# metrics/kpi-definitions.yaml
kpis:
  mttd:
    name: Mean Time to Detect
    description: Average time from vulnerability introduction to detection
    target: "< 24 hours"
    source: scanner_results + commit_history

  mttr:
    name: Mean Time to Remediate
    description: Average time from detection to fix deployed
    target_critical: "< 24 hours"
    target_high: "< 7 days"
    target_medium: "< 30 days"
    source: issue_tracker

  scan_coverage:
    name: Scan Coverage
    description: Percentage of repositories with active security scanning
    target: "> 95%"
    scan_types: [sast, sca, container, iac]

  security_debt:
    name: Security Debt Score
    description: Weighted sum of open findings (critical=10, high=5, medium=2, low=1)
    target: "< 50"
    trend: decreasing

  fix_rate:
    name: Fix Rate
    description: Percentage of findings fixed vs. accepted risk
    target: "> 80%"

  false_positive_rate:
    name: False Positive Rate
    description: Percentage of findings marked as false positive
    target: "< 15%"
```

## Workflow

### Step 1: Collect Metrics from Scans

```bash
node scripts/agent.js --action collect --source-dir . --output /tmp/metrics.json
```

### Step 2: Calculate KPIs

```bash
node scripts/agent.js --action kpis --metrics /tmp/metrics.json --output /tmp/kpis.json
```

### Step 3: Generate Dashboard Data

```bash
node scripts/agent.js --action dashboard --output /tmp/dashboard.json
```

## Verification

- [ ] Metrics collected daily from all scan sources
- [ ] MTTD and MTTR tracked per severity level
- [ ] Scan coverage tracked per scan type (SAST, SCA, container, IaC)
- [ ] Security debt score trending downward
- [ ] Dashboard accessible to engineering and security teams
- [ ] Monthly security metrics review meeting established

## References

- [Prometheus Documentation](https://prometheus.io/docs/)
- [Grafana Dashboard Best Practices](https://grafana.com/docs/grafana/latest/dashboards/)
- [OWASP DevSecOps Metrics](https://owasp.org/www-project-devsecops-guideline/)
- [DORA Metrics](https://dora.dev/research/)
