Report Types & Engine
Sidebar: Reports (/reports)
This is the in-depth technical companion to the Reports overview. It documents the reporting engine (how a report is built and certified), the full catalogue of report types the platform ships, the export formats each one renders to, the filters that scope every report, multi-language support, and the API surface.
If you only want to generate a report from the UI, start with the Reports overview. If you want to understand exactly what numbers end up in a report and why they reconcile with the live dashboard, read on.
Engine design: one source of truth -> builders -> adapters
The reporting engine separates what data goes in a report from how it is rendered. Every report follows the same three-stage pipeline.
Why metrics are the single source of truth. Before any builder runs, the
engine resolves the requested window and scope, then calls
metrics_service.kpis() once. That bundle (overall, severity_distribution,
risk_by_app, kev_ratio, reachability_ratio, burndown,
introduced_vs_resolved, scan_coverage, ...) is the only numeric input every
builder sees. Because the same service powers the live Reports dashboard, the
numbers in a generated report reconcile exactly with the UI for the same period
and scope.
Builders turn metrics (plus, where needed, direct vulnerability/finding
queries) into a format-neutral ReportPayload. A payload is a list of
Sections, each with a kind (narrative, kpi_grid, table,
finding_list, findings_detail, chart, control_index, raw_html) and
optional ChartSpecs (donut, line, bar, stacked_bar, burndown). The
payload is the single contract between builders and adapters - add a section and
every export format renders it automatically.
Adapters consume the ReportPayload and emit (bytes, mime). The HTML
adapter produces the branded HTML; the PDF adapter feeds that same HTML through
WeasyPrint, so PDF and HTML are visually identical (cover, table of contents,
charts, integrity footer).
Integrity / tamper-evidence
After the builder runs, the engine computes a SHA-256 data hash over
{type, audience, snapshot_at, metrics} and stores it on the ReportRun. The
hash certifies the numbers as of the snapshot timestamp and is reproducible
from the persisted run without re-running the builder. The
GET /reports/runs/{id}/verify endpoint recomputes it and reports match,
which is what the Verify integrity badge in the run history calls.
Each completed run is persisted as a ReportRun (content bytes, MIME,
content_hash, byte_size, frozen metrics, snapshot_at, expires_at) with
a default 90-day retention and an audit-log entry for both generation and
download.
Report types
The engine registers builders in engine.BUILDERS. The following identifiers
are the real type values you pass to the API and that appear on a ReportRun.
SBOM is handled by a dedicated path (_generate_sbom) rather than a builder, but
is requested the same way.
Type (identifier) | Title | Default audience | Purpose |
|---|---|---|---|
executive | Executive Security Report | Board | One-page posture: score + trend, risk by business unit, top risks, period delta |
technical | Technical Security Report | Engineering | Full detail: KPIs, severity, by-app, KEV/reachability/exploitable, top vulns + per-vuln dossier |
compliance | Compliance & Audit Report | Auditor | Per-framework control coverage, gaps, risk acceptances, evidence |
scan | Scan Report | Engineering | Single engagement: findings + compliance mapping for one scan |
vuln_detail | Vulnerability Detail Report | Engineering | Single-finding dossier: evidence chain + control mapping |
trend | Security Trend Report | Board | Burndown, introduced vs resolved, MTTR/score over time |
remediation_sla | Remediation & SLA Report | Ops | Open vs closed, overdue, aging buckets, MTTR & SLA by team |
findings | Findings Register | Engineering | Flat, filtered vulnerability list - the CSV/XLSX workhorse export |
delta | Baseline / Delta Report | Ops | A-vs-B period comparison: new / resolved / regressed |
remediation_plan | Remediation Plan | Engineering | Open vulns grouped by the single fix that resolves them, ranked by risk reduced |
sca | Software Composition (SCA) Report | Engineering | Dependency-centric: top vulnerable components, fixed-version availability, reachability |
sast | Static Analysis (SAST) Report | Engineering | Weakness-centric: by-CWE clustering, most-affected files, reachability |
secrets | Secrets Exposure Report | Engineering | Exposed secrets with type, location and severity |
attack_chains | Attack Chains & Toxic Combinations | Engineering | Ranked multi-hop kill-chains and toxic combinations |
exposure | Portfolio Exposure & Coverage | Board | Coverage matrix (env x criticality), density, health buckets, blind spots |
sbom | SBOM (CycloneDX / SPDX / VEX) | Engineering | Machine-readable software bill of materials, optionally with vulnerabilities |
The /reports gallery curates a smaller, audience-oriented set
(executive, exposure, technical, findings, compliance,
remediation_sla, trend, scan, vuln_detail). The discipline reports
(sca / sast / secrets) are reachable via Technical + the source filter,
delta folds into Trend, remediation_plan folds into Remediation & SLA,
and SBOM/VEX export lives on the Code -> SCA page. Every type above is still a
valid type for the POST /reports/generate API and for scheduled definitions.
Core posture reports
- Executive - the leadership one-pager: posture score and grade, risk by business unit, top-risk applications, and the period delta.
- Technical - the AppSec workhorse. A KPI grid (open, exploit-confirmed,
KEV %, reachable %, false-positive rate, MTTR), a severity donut, risk-by-app
table, introduced-vs-resolved chart, a top-open-vulnerabilities summary, and a
per-vulnerability dossier (
findings_detail) carrying description, evidence, remediation, CVE/CWE, component and reachability for the top items. Closes with the risk methodology narrative so the scoring is auditable. - Compliance - per-framework control coverage and gaps, risk acceptances and
the evidence chain; takes a
frameworksselection (OWASP Top 10, PCI-DSS v4, ISO 27001, SOC 2, NIST 800-53, HIPAA, GDPR). See Compliance & Maturity. - Trend / Remediation & SLA - burndown, introduced-vs-resolved, MTTR, aging buckets, overdue (SLA-breached) and per-team remediation efficiency.
- Scan / Vulnerability detail - narrow, subject-scoped reports for a single
scan (
scan_id) or a single vulnerability (vulnerability_id).
Findings Register (findings)
The industry-standard "detailed findings" export. It honors the same scope +
discipline + severity pipeline as every other report (via
metrics_service._load_vulns) and adds a lifecycle-status filter on top
(open by default). One row per correlated vulnerability,
sorted by severity then composite priority, with severity / title / application /
OWASP / CWE / CVE / location / sources / status / flags (KEV.EXPL.REACH) /
priority / first-seen. Because it renders as plain table sections, every adapter
produces a usable register - it is the canonical CSV/XLSX export.
Discipline reports (sca / sast / secrets)
These are auto-scoped to a single testing discipline via load_discipline() and
present a discipline-native view:
- SCA - top vulnerable components grouped by
component@version, worst severity, fixed-version availability and reachability. Pairs with the SBOM. - SAST - clustering by CWE and a "most-affected files" table, with reachable-finding counts.
- Secrets - exposed secrets with type, application, location (
file:line) and lifecycle status.
Attack Chains (attack_chains)
Multi-hop attack-chain inference over correlated vulnerabilities (the ASPM
differentiator). It calls attack_chain_service.build_chains(), scoped to a
single application when the report scope resolves to exactly one app, otherwise
org-wide. The report ranks kill-chains and toxic combinations by score, shows
their hops/MITRE stages, and a danger flag for internet-facing chains. See
Attack Chains.
Exposure (exposure)
Portfolio coverage and asset blind-spots: a coverage matrix (environment x criticality), vulnerability density, portfolio health buckets, cloud-native source exposure (container / IaC / secrets), top-risk assets, and the list of business-critical applications that have not been scanned in the period.
Delta / Baseline (delta)
A point-in-time A-vs-B comparison that proves remediation effectiveness. It compares the period start (baseline) with the period end (current) and buckets each vulnerability in scope into newly introduced, resolved, or regressed (reopened).
Remediation Plan (remediation_plan)
Groups open vulnerabilities by the single remediation that resolves them
("upgrade lodash -> 12 vulns gone"). SCA dependency vulns with a known fixed
version group as Upgrade <pkg> -> <version>; everything else groups by
vulnerability class / CWE with a representative fix. Groups are ranked by risk
reduced, so engineering fixes the highest-leverage items first.
SBOM (sbom)
The SBOM path produces a machine-readable bill of materials via sbom_service.
The format parameter selects the standard: cyclonedx, spdx, or vex
(vulnerable components only). For CycloneDX/SPDX the engine first attempts a
full SBOM via a live repo scan when the scope resolves to a single
application with a connected source repo; otherwise it falls back to a hub
SBOM built from the persisted SCA findings. The artifact is always emitted as
JSON.
Export formats
Adapters are registered in engine.ADAPTERS. Non-SBOM reports support five
formats; SBOM emits its three JSON dialects.
| Format | Adapter | MIME | Notes |
|---|---|---|---|
pdf_adapter | application/pdf | Renders the HTML through WeasyPrint - identical to HTML output. Default. | |
| HTML | html_adapter | text/html | Branded HTML: cover, TOC, charts, integrity footer. Served inline. |
| CSV | csv_adapter | text/csv; charset=utf-8 | Flattens KPI grids, tables and the control index. UTF-8-SIG for Excel. |
| XLSX | xlsx_adapter | ...spreadsheetml.sheet | One sheet per table/KPI section + Summary + Control Index. |
| JSON | json_adapter | application/json | Canonical payload (sections + metrics + integrity hash) for BI/API. Served inline. |
| CycloneDX / SPDX / VEX | sbom_service | application/json | SBOM dialects (sbom type only). |
If you request a format that does not apply to a given type, the engine
normalizes it to pdf (or raises for SBOM dialects on non-SBOM types).
Filters & scoping
Every report is driven by the same filter dimensions, applied before metrics are computed so all numbers stay consistent across types and formats.
- Scope -
apps/projects(a target selection rolls up to its application). Omit for org-wide. - Sources (disciplines) - narrow to a subset of testing disciplines (
dast,sast,sca,secrets,iac,container,imported). Selecting all (or none) means whole-program; a strict subset adds an explicit "Report scope" note to the report so the reader knows the numbers are not whole-program. Thesca/sast/secrets/attack_chains/sbomtypes fix their own discipline and hide the picker. - Severity - used as a scope filter; the Findings Register also exposes it as a first-class UI filter.
- Status - the Findings Register lifecycle filter, with
open/all/closedpresets (defaults to open). - Window -
7d,30d,90d,ytd,all, or an explicitstart/end. Point-in-time reports (findings,remediation_plan,sca,sast,secrets,attack_chains,sbom) ignore the period selector.
Multi-language (i18n)
Report chrome - titles, section headers, KPI/column labels, audience names
and the risk-methodology narrative - is localized via
reporting/i18n.py. Supported languages today are English (en) and
Azerbaijani (az); t(key, lang) falls back to EN then to the raw key, so
adding a third language is a matter of adding values. Dynamic finding text
(titles, evidence, AI narratives) stays in English. Pass language on the
generate request or store it on a scheduled definition.
API
All endpoints live under the reports router (/api/v1/reports). Generation and
read endpoints require view_findings; downloads/shares require
export_evidence; managing scheduled definitions requires manage_reports.
| Method | Path | Purpose |
|---|---|---|
GET | /reports/metrics | KPI bundle (the single source of truth) for the given window/scope |
POST | /reports/generate | Ad-hoc generation -> returns a ReportRun |
GET | /reports/runs | Run history (metadata), filterable by type / status |
GET | /reports/runs/{id} | Run metadata + frozen metrics |
GET | /reports/runs/{id}/download | Artifact bytes (audit-logged) |
GET | /reports/runs/{id}/verify | Tamper-evidence recheck (recompute data hash) |
POST | /reports/runs/{id}/share | Create an expiring read-only share link |
GET | /reports/shares | List share links |
DELETE | /reports/shares/{id} | Revoke a share link |
GET | /reports/definitions | List scheduled report definitions |
POST | /reports/definitions | Create a scheduled definition |
PATCH | /reports/definitions/{id} | Update a definition / schedule |
POST | /reports/definitions/{id}/run-now | Generate one run from a definition immediately |
DELETE | /reports/definitions/{id} | Delete a definition |
GET | /r/{run_id}?t=<token> | Public read-only viewer (no auth; validated by the expiring token) |
A minimal generate request body: type, format, optional audience,
period / start / end, apps / projects, severity, sources,
statuses (findings), frameworks (compliance), scan_id / vulnerability_id
(subject reports), language, and branding_on.
Where it lives in the UI
The hub is Reports at /reports: a live metrics dashboard (score ring,
KPIs, severity donut, burndown), the report gallery (click a card to open the
generate modal with the type's scope/period/discipline/severity/status/framework
controls), the scheduled reports panel, and the run history table with
per-run download, integrity-verify and share actions. The
Exposure dashboard is linked from the header.