Vulnerability csv reporting
[COLM'26] SkillLearnBench is the first benchmark for evaluating continual learning methods that automatically generate agent skills.
npx -y skills add cxcscmu/SkillLearnBench --skill vulnerability-csv-reportingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
Generate structured CSV security audit reports from Trivy or other vulnerability scanner JSON output. Use this skill whenever the user asks to write vulnerability results to a CSV file, create a security audit report, or export CVE findings with fields like Package, Version, CVE_ID, Severity, CVSS_Score, Fixed_Version, Title, Url.
SKILL.md
4.1 KB, as published. Nobody here has run it
Vulnerability CSV Reporting
Convert vulnerability scan JSON output into a well-structured CSV report using Python's
csv.DictWriter for reliable quoting and escaping.
CSV Schema
Standard security audit columns:
| Column | Source in Trivy JSON | Notes |
|---|---|---|
| Package | PkgName | e.g. "lodash" |
| Version | InstalledVersion | e.g. "4.17.20" |
| CVE_ID | VulnerabilityID | e.g. "CVE-2021-23337" |
| Severity | Severity | HIGH or CRITICAL |
| CVSS_Score | CVSS.nvd.V3Score (with fallback) | float or "N/A" |
| Fixed_Version | FixedVersion | version string or "N/A" if missing |
| Title | Title (fallback: Description[:80]) | Short description of the vuln |
| Url | References[0] | First reference URL or "N/A" |
Complete Python script
import csv
import json
FIELDNAMES = ["Package", "Version", "CVE_ID", "Severity", "CVSS_Score",
"Fixed_Version", "Title", "Url"]
def extract_cvss_score(vuln):
cvss = vuln.get("CVSS", {})
for source in ["nvd", "ghsa", "redhat"]:
scores = cvss.get(source, {})
if scores.get("V3Score") is not None:
return str(scores["V3Score"])
if scores.get("V2Score") is not None:
return str(scores["V2Score"])
for source, scores in cvss.items():
for key in ["V3Score", "V2Score"]:
if scores.get(key) is not None:
return str(scores[key])
return "N/A"
def vuln_to_row(vuln):
title = vuln.get("Title") or vuln.get("Description", "")[:120]
refs = vuln.get("References") or []
url = refs[0] if refs else "N/A"
return {
"Package": vuln.get("PkgName", "N/A"),
"Version": vuln.get("InstalledVersion", "N/A"),
"CVE_ID": vuln.get("VulnerabilityID", "N/A"),
"Severity": vuln.get("Severity", "N/A"),
"CVSS_Score": extract_cvss_score(vuln),
"Fixed_Version": vuln.get("FixedVersion") or "N/A",
"Title": title,
"Url": url,
}
def write_csv(results_path, output_path, severities=("HIGH", "CRITICAL")):
with open(results_path) as f:
data = json.load(f)
rows = []
for result in data.get("Results", []):
for vuln in result.get("Vulnerabilities") or []:
if vuln.get("Severity") in severities:
rows.append(vuln_to_row(vuln))
with open(output_path, "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=FIELDNAMES)
writer.writeheader()
writer.writerows(rows)
print(f"Wrote {len(rows)} vulnerabilities to {output_path}")
write_csv("/root/trivy-results.json", "/root/security_audit.csv")
Key patterns
- Use
csv.DictWriter— it handles quoting/escaping automatically FixedVersionis often absent; always use.get("FixedVersion") or "N/A"Referencesmay be None or empty list; guard withor []Titlecan be empty; fall back to truncatedDescription- Filter severity before writing — don't include LOW/MEDIUM
Deduplication (optional)
If the same CVE appears in multiple result targets, deduplicate on (CVE_ID, Package, Version):
seen = set()
for vuln in vulns:
key = (vuln["VulnerabilityID"], vuln["PkgName"], vuln["InstalledVersion"])
if key not in seen:
seen.add(key)
rows.append(vuln_to_row(vuln))