agentsclimarketplace

Vulnerability csv reporting

Skill cxcscmu/SkillLearnBench/skills/b4-skill-creator-claude-sonnet-4-6/dependency-vulnerability-check/vulnerability-csv-reporting

[COLM'26] SkillLearnBench is the first benchmark for evaluating continual learning methods that automatically generate agent skills.

Install
npx -y skills add cxcscmu/SkillLearnBench --skill vulnerability-csv-reporting

Assembled 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:

ColumnSource in Trivy JSONNotes
PackagePkgNamee.g. "lodash"
VersionInstalledVersione.g. "4.17.20"
CVE_IDVulnerabilityIDe.g. "CVE-2021-23337"
SeveritySeverityHIGH or CRITICAL
CVSS_ScoreCVSS.nvd.V3Score (with fallback)float or "N/A"
Fixed_VersionFixedVersionversion string or "N/A" if missing
TitleTitle (fallback: Description[:80])Short description of the vuln
UrlReferences[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
  • FixedVersion is often absent; always use .get("FixedVersion") or "N/A"
  • References may be None or empty list; guard with or []
  • Title can be empty; fall back to truncated Description
  • 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))

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.