Visualizing Infrastructure Security at Software Project Inception

Headshot of Luiz Antunes.

The earliest stage of a software project carries engineering risks that are easy to overlook. The software does not yet exist, and the team is busy standing up infrastructure: provisioning servers, writing automation scripts, configuring access controls, and establishing the scaffolding that everything else will run on. The code that does this work—Terraform templates, Ansible playbooks, shell scripts, Dockerfiles—is software too, and it has vulnerabilities.

The potential issue at this stage is specific: Scripts that create infrastructure can be exploited to open back doors. A misconfigured Identity and Access Management (IAM) role, an exposed port left open in a provisioning script, or an unpatched base image can quietly become an entry point that persists through every phase of the lifecycle that follows. Because these issues are introduced before development begins in earnest, they tend not to appear in the usual development metrics—no sprint tickets, no code review comments. They can sit undetected for a long time.

The good news is that project inception is one of the most instrumentation-friendly stages in the entire lifecycle. As this post illustrates, vulnerability scanning is a well-understood problem with mature tooling, and the output of that tooling is exactly the kind of structured, time-series data that lends itself to effective visualization.

What to Measure

The useful metric at the inception and project configuration stage is the infrastructure vulnerability report: This is a record of which known vulnerabilities (CVEs) are present in your infrastructure, at what levels of severity, and how that picture is changing over time.

A single vulnerability report is a snapshot. What we really want is a series of snapshots—one per scan—so we can answer questions like the following:

  • Are new vulnerabilities appearing faster than we are resolving them?
  • Is a particular CVE recurring after we thought it was patched?
  • Are certain components consistently responsible for the bulk of our exposure?

The time dimension is what turns a security report into a monitoring tool.

The Visualization: A CVE Presence Heat Map

One of the most effective ways to display this information is through a heat map with CVEs on one axis and scan dates on the other. In this visualization, each cell represents whether a given vulnerability was detected on a given date, and the cell's color encodes its severity. The result resembles something like the heat map in Figure 1.

figure1_08202026
H = High severity (red), M = Medium (orange), L = Low (yellow), [ ] = not detected

What makes this format powerful is that it exposes patterns that a static snapshot cannot. A row where the same CVE lights up on alternating dates suggests a remediation that is not sticking—the vulnerability is being patched and reintroduced. A column that goes suddenly dense with high-severity findings suggests that a base image update introduced a batch of new issues. A CVE that appears once and never again is almost certainly resolved; one that keeps appearing is a candidate for escalation.

The human visual system is exceptionally good at detecting these kinds of patterns in a grid. Presented as a sorted table of CVE IDs and severity scores, the same data would require careful reading. Presented as a heat map, the patterns are immediately visible.

Please note that in the illustration above the CVE change rate is artificially shown as occurring daily to show how the visualization should work. In reality, changes are more subtle and spaced in time. The actual rate increases based on the number of dependencies within a project. Any changes to the number of dependencies within a project may result in increased vulnerabilities.

Getting the Data

If you want to play with this visualization, you will need two things: a vulnerability scanner and a way to store its output over time.

Scanning your infrastructure with Trivy

Trivy is a free, open-source vulnerability scanner that works against container images, filesystems, Git repositories, and infrastructure as code (IaC) files (e.g., Terraform, Dockerfile, Helm charts). It produces structured JSON output that maps directly to what we need.

To scan a container image, type the command

    




trivy image --format json --output results.json your-base-image:latest

  

Similarly, to scan an IaC directory, you can type

    




trivy config --format json --output results.json ./infrastructure/

  

The JSON output includes CVE IDs, severity ratings, affected packages, and fix availability. A lightweight Python script can parse this output and append a dated record to a running log—one row per CVE per scan date.

Building the Time-series Log

The goal of the following code sample is to demonstrate one path to accomplishing an action. To incorporate it into production and capture any additional conditions, it would most likely have to be developed further.

    




import json
import csv
from datetime import date

def append_scan_results(results_file, log_file):
    scan_date = date.today().isoformat()
    
    with open(results_file) as f:
        results = json.load(f)
    
    rows = []
    for result in results.get("Results", []):
        for vuln in result.get("Vulnerabilities", []):
            rows.append({
                "date": scan_date,
                "cve_id": vuln.get("VulnerabilityID"),
                "severity": vuln.get("Severity"),
                "package": vuln.get("PkgName"),
                "fixed_version": vuln.get("FixedVersion", "none")
            })
    
    if not rows:
        print("No vulnerabilities found. Nothing written to the log.")
        return

    with open(log_file, "a", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=rows[0].keys())
        writer.writerows(rows)

append_scan_results("results.json", "vulnerability_log.csv")

  

Run this script after each scan—ideally as a step in your continuous integration (CI) pipeline—and over time you will accumulate exactly the data you need to build the heat map.

Rendering the heat map

With the log in hand, a few lines of Python using packages pandas and seaborn will produce the visualization:

    




import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

df = pd.read_csv("vulnerability_log.csv")

# Pivot to a matrix: CVEs as rows, dates as columns
# Use severity as the cell value (encode as numeric for color mapping)
severity_map = {"CRITICAL": 4, "HIGH": 3, "MEDIUM": 2, "LOW": 1}
df["severity_score"] = df["severity"].map(severity_map)

matrix = df.pivot_table(
    index="cve_id",
    columns="date",
    values="severity_score",
    aggfunc="max"
).fillna(0)

plt.figure(figsize=(14, 8))
sns.heatmap(
    matrix,
    cmap=["#f5f5e8", "#ffffcc", "#f4a460", "#e05c5c", "#8b0000"],
    linewidths=0.5,
    linecolor="#cccccc"
)
plt.title("CVE Presence Over Time")
plt.tight_layout()
plt.savefig("cve_heatmap.png", dpi=150)

  

If your team uses a different scanner—OPENVAS, Grype, Snyk—the structure is the same: extract CVE ID, severity, and date; build the pivot table; render the heat map. The scanner is interchangeable; the visualization pattern is not.

What to Watch For

Once your heat map is running, a few patterns are worth calling out explicitly to your team:

Recurring rows. A CVE that disappears and reappears is a remediation problem, not a detection problem. The fix is not being applied consistently—perhaps it lives in a base image that gets periodically reset, or the fix is being applied in one environment but not another.

Dense columns. A scan date with an unusually high concentration of new findings often correlates with a base image update, a new dependency being added to the infrastructure stack, or a newly published batch of CVEs. It is worth correlating these columns with your infrastructure change log.

Long-lived high-severity rows. A high or critical CVE that persists across many dates without resolution deserves explicit escalation. Heat maps make these visible at a glance in a way that a sorted report does not.

Rows that clear and stay clear. These are your wins. A CVE that disappears and stays gone is evidence that your remediation process is working. Do not ignore the good news—it calibrates your team's sense of what "normal" looks like.

Fitting a CVE Heat Map into Your Workflow

The ideal integration is a scheduled scan that runs whenever infrastructure code changes, either on commit to the infrastructure repository or, at a minimum, on a nightly cron schedule. The output gets appended to the log, and the heat map regenerates automatically.

In a continuous integration and continuous delivery (CI/CD) context, you can configure the scan to fail the pipeline if any critical-severity CVEs are detected in newly introduced infrastructure code, while allowing lower-severity findings to pass through to the log for monitoring. This creates a hard gate for the most serious issues while maintaining visibility across the full vulnerability landscape.

The key principle is that the scan should run on a schedule, not just when someone remembers to run it. The value of the heat map comes from the time series. A one-time scan produces a snapshot, but it is the accumulation of scans over time that produces the pattern recognition capability we are after.

Coming Up Next in Information Visualization in DevOps

This is the second post in a series, Information Visualization in DevOps. If you have not read the introduction, start there for an overview of the series and the monitoring framework we will be building toward.

In the next post, we will move deeper into the development cycle and look at the Code/Commit/CI phase. The risk there is different: not external vulnerabilities, but the complexity that comes from the organic growth of a codebase itself. We will explore how to visualize commit patterns, codebase growth by component, and sprint velocity in ways that surface early warning signs of technical risk before it manifests as failures downstream.

This post is part of the Information Visualization in DevOps series. Read the first post in the series, Information Visualization as a DevOps Monitoring Tool.

Additional Resources

SEI Blog Post: Information Visualization as a DevOps Monitoring Tool by Luiz Antunes.

Get updates on our latest work.

Each week, our researchers write about the latest in software engineering, cybersecurity and artificial intelligence. Sign up to get the latest post sent to your inbox the day it's published.

Subscribe Get our RSS feed