Skip to main content

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)TitleDefault audiencePurpose
executiveExecutive Security ReportBoardOne-page posture: score + trend, risk by business unit, top risks, period delta
technicalTechnical Security ReportEngineeringFull detail: KPIs, severity, by-app, KEV/reachability/exploitable, top vulns + per-vuln dossier
complianceCompliance & Audit ReportAuditorPer-framework control coverage, gaps, risk acceptances, evidence
scanScan ReportEngineeringSingle engagement: findings + compliance mapping for one scan
vuln_detailVulnerability Detail ReportEngineeringSingle-finding dossier: evidence chain + control mapping
trendSecurity Trend ReportBoardBurndown, introduced vs resolved, MTTR/score over time
remediation_slaRemediation & SLA ReportOpsOpen vs closed, overdue, aging buckets, MTTR & SLA by team
findingsFindings RegisterEngineeringFlat, filtered vulnerability list - the CSV/XLSX workhorse export
deltaBaseline / Delta ReportOpsA-vs-B period comparison: new / resolved / regressed
remediation_planRemediation PlanEngineeringOpen vulns grouped by the single fix that resolves them, ranked by risk reduced
scaSoftware Composition (SCA) ReportEngineeringDependency-centric: top vulnerable components, fixed-version availability, reachability
sastStatic Analysis (SAST) ReportEngineeringWeakness-centric: by-CWE clustering, most-affected files, reachability
secretsSecrets Exposure ReportEngineeringExposed secrets with type, location and severity
attack_chainsAttack Chains & Toxic CombinationsEngineeringRanked multi-hop kill-chains and toxic combinations
exposurePortfolio Exposure & CoverageBoardCoverage matrix (env x criticality), density, health buckets, blind spots
sbomSBOM (CycloneDX / SPDX / VEX)EngineeringMachine-readable software bill of materials, optionally with vulnerabilities
UI vs. engine

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 frameworks selection (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.

FormatAdapterMIMENotes
PDFpdf_adapterapplication/pdfRenders the HTML through WeasyPrint - identical to HTML output. Default.
HTMLhtml_adaptertext/htmlBranded HTML: cover, TOC, charts, integrity footer. Served inline.
CSVcsv_adaptertext/csv; charset=utf-8Flattens KPI grids, tables and the control index. UTF-8-SIG for Excel.
XLSXxlsx_adapter...spreadsheetml.sheetOne sheet per table/KPI section + Summary + Control Index.
JSONjson_adapterapplication/jsonCanonical payload (sections + metrics + integrity hash) for BI/API. Served inline.
CycloneDX / SPDX / VEXsbom_serviceapplication/jsonSBOM 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. The sca / sast / secrets / attack_chains / sbom types 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 / closed presets (defaults to open).
  • Window - 7d, 30d, 90d, ytd, all, or an explicit start/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.

MethodPathPurpose
GET/reports/metricsKPI bundle (the single source of truth) for the given window/scope
POST/reports/generateAd-hoc generation -> returns a ReportRun
GET/reports/runsRun history (metadata), filterable by type / status
GET/reports/runs/{id}Run metadata + frozen metrics
GET/reports/runs/{id}/downloadArtifact bytes (audit-logged)
GET/reports/runs/{id}/verifyTamper-evidence recheck (recompute data hash)
POST/reports/runs/{id}/shareCreate an expiring read-only share link
GET/reports/sharesList share links
DELETE/reports/shares/{id}Revoke a share link
GET/reports/definitionsList scheduled report definitions
POST/reports/definitionsCreate a scheduled definition
PATCH/reports/definitions/{id}Update a definition / schedule
POST/reports/definitions/{id}/run-nowGenerate 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.

See also