composing-vulnerability-report

Read findings JSONL files from cluster 1-4 skills, deduplicate by fingerprint, group by severity, and compose a deliverable- grade markdown vulnerability report with per-finding sections (title, severity, target, detail, remediation, evidence) and a top-level summary table. The canonical written artifact a customer receives at engagement close; precise, reproducible, machine- checkable against source findings. Use when: closing an engagement, generating an interim report, regenerating after CVE or OWASP enrichment, or producing the input for generating-executive-summary. Threshold: findings missing required fields are dropped. HIGH and CRITICAL findings highlighted in the summary section. Trigger with: "compose vuln report", "write pentest report", "generate vulnerability deliverable", "render findings to report".

4 Tools
penetration-tester Plugin
security Category

Allowed Tools

ReadWriteBash(python3:*)Glob

Provided by Plugin

penetration-tester

25-skill pentest pack with engagement governance, network/code/dependency scans, OWASP Top 10 mapping, and exec-readable reporting. Heavy-hitter compliant; chain-of-custody attestable.

security v3.0.0
View Plugin

Installation

This skill is included in the penetration-tester plugin:

/plugin install penetration-tester@claude-code-plugins-plus

Click to copy

Instructions

Composing Vulnerability Report

Overview

After cluster 1-4 scan skills run, each one produces a Findings

file. A typical engagement ends up with eight to twenty such files

across the different skill categories. The customer wants ONE

vulnerability report — comprehensive, deduplicated, organized by

severity, with each finding cross-referenced to its source skill

and target.

This skill consumes one or more findings files (JSONL preferred,

JSON list also accepted), deduplicates entries by the canonical

fingerprint defined in lib/finding.py, enriches each finding

with a CVSS v3.1 vector when one isn't present (using a deterministic

heuristic based on severity + category — explicitly noted as

"derived, not assigned by NVD" in the output), and emits a single

markdown report with per-finding sections plus a top-level

summary table.

The report has a defined structure that downstream tools (next

two skills in cluster 6) consume:

  1. Header — engagement ID, generation timestamp, source files
  2. Summary table — finding count by severity
  3. Per-severity sections — CRITICAL first, then HIGH, MEDIUM,

LOW, INFO

  1. Per-finding subsections — title, severity, target, detail,

remediation, evidence, references

When the skill produces findings

Finding Severity Threshold Affected control
Source file unparseable HIGH JSON/JSONL parse fails (operational)
Finding missing required field HIGH A finding record is missing title, severity, target, detail, or remediation (operational)
Duplicate fingerprint across files INFO Same finding appears in N>1 sources; reported as deduplication count (informational)
Source file has zero findings INFO Empty or all-info-only file; reported but not an error (informational)
Report generated cleanly INFO Positive confirmation (informational)

Prerequisites

  • Python 3.9+
  • One or more findings files in JSON or JSONL format produced by

any cluster 1-4 scan skill (which all share lib/finding.py

schema)

Instructions

Step 1 — Gather findings sources

By default the skill reads every file matching

engagement/findings/.json and engagement/findings/.jsonl.

Override with --source FILE (repeatable).

Step 2 — Run the composer


python3 ./scripts/compose_report.py engagements/acme-2026-q2/

Options:


Usage: compose_report.py PATH [OPTIONS]

Options:
  --source FILE         Specific findings file (repeatable; overrides default glob)
  --report-output FILE  Write the composed report here (default:
                        PATH/reports/vulnerability-report.md)
  --engagement-id ID    Override the engagement ID (default: parse from PATH/roe.yaml)
  --output FILE         Operational findings output (this skill's own findings)
  --format FMT          json | jsonl | markdown (default: markdown)
  --min-severity SEV    Filter report to findings at or above this severity
  --include-info        Include INFO-severity findings in the report (default: omit)

Step 3 — Review the report

The output report has a predictable structure. The header

identifies the engagement, the source files, and the generation

timestamp. The summary table shows finding counts by severity.

Per-severity sections follow.

Each finding subsection includes a stable anchor (the fingerprint)

so cross-references from later artifacts (executive summary,

OWASP mapping) resolve into the report cleanly.

Step 4 — Verify against sources


python3 ./scripts/compose_report.py engagements/acme-2026-q2/ --format json --output /tmp/compose-findings.json
jq '.[] | select(.severity == "high")' /tmp/compose-findings.json

If the report references a finding the operator didn't expect,

trace back via the finding's skill_id + target to the source

file.

Examples

Example 1 — End-of-engagement report


python3 ./scripts/compose_report.py engagements/acme-2026-q2/ \
    --report-output engagements/acme-2026-q2/reports/vulnerability-report.md

Example 2 — Interim report mid-engagement


python3 ./scripts/compose_report.py engagements/acme-2026-q2/ \
    --min-severity high \
    --report-output engagements/acme-2026-q2/reports/interim-2026-06-15.md

--min-severity high produces an interim report covering only

HIGH and CRITICAL findings — useful for in-engagement customer

syncs.

Example 3 — Regenerate after OWASP mapping


python3 ./scripts/compose_report.py engagements/acme-2026-q2/ \
    --source engagements/acme-2026-q2/findings/all-findings-with-owasp.jsonl \
    --report-output engagements/acme-2026-q2/reports/vulnerability-report-v2.md

Re-run after mapping-findings-to-owasp-top10 has enriched each

finding with its OWASP category; the regenerated report includes

the OWASP tag in each per-finding subsection.

Output

JSON / JSONL / Markdown per lib/report.py for the skill's own

operational findings. The PRIMARY output is the composed

vulnerability report, written as standalone Markdown to the

--report-output path.

Each operational Finding includes:

  • idcompose::::
  • severity — CRITICAL / HIGH / MEDIUM / INFO
  • categoryreport-composition
  • summary — what went wrong (or right) during composition
  • evidence — source files, finding counts, dedup stats

Error Handling

  • PATH missing or empty → emits CRITICAL operational finding,

exits 1.

  • Source file unparseable → emits HIGH operational finding,

skips the file, continues with remaining sources.

  • No findings files found → emits HIGH operational finding,

exits 1.

  • Output report path not writable → emits HIGH operational

finding, exits 1.

  • Finding missing required fields → emits HIGH operational

finding, omits that record from the report, continues.

Resources

  • references/THEORY.md — Vulnerability-report structure history

(NIST SP 800-115, OWASP Testing Guide), CVSS v3.1 vector

composition, severity scoring tradeoffs (CVSS vs intrinsic vs

EPSS), finding-deduplication theory, why fingerprint-based dedup

beats title-based

  • references/PLAYBOOK.md — Report-template variants per

audience (technical, executive, regulatory), per-finding

remediation phrasing patterns, evidence-redaction patterns

for distributed reports, cross-reference protocol with the

OWASP-mapping and exec-summary skills

Ready to use penetration-tester?