Generate a NIS2 compliance report¶
GET /compliance/nis2-report.pdf renders a downloadable PDF combining CVE
exposure posture, SLA compliance, and recent security decisions — intended
to be shared with auditors, management, or a compliance officer. This guide
covers generating it, scoping it to a period and/or a subset of machines,
and what the [À VALIDER: …] placeholder blocks mean.
Two other formats of the same underlying data exist alongside the PDF:
GET /compliance/nis2-posture (JSON metrics only) and
GET /compliance/nis2-report.json (a fuller JSON audit package). This guide
covers the PDF, since it's the one meant to leave Repod as a standalone
document.
1. Prerequisites¶
- Any authenticated role (
GET /compliance/nis2-report.pdfusesget_current_user, not an elevated role) — but the caller's own machine access restrictions still apply: a scoped user's report is limited to machines they can see, never widened by the query parameters below. - The
nis2_reportlicense feature must be entitled. - WeasyPrint renders the PDF server-side — no client-side dependency, just
a plain
curl/browser download.
2. Generate the report (default: whole fleet, live snapshot)¶
curl -s http://localhost:8000/api/v1/compliance/nis2-report.pdf \
-H "Authorization: Bearer $TOKEN" \
-o nis2_compliance_report.pdf
With no query parameters, the report covers every enabled machine the caller has access to, and the security-decisions history section covers all recorded decisions with no date filter.
3. Scope by period¶
period_start / period_end (ISO 8601, e.g. 2026-01-01) filter only
the security-decisions history section (accept/reject/exception/upgrade
decisions recorded in that window).
curl -s "http://localhost:8000/api/v1/compliance/nis2-report.pdf?period_start=2026-01-01&period_end=2026-06-30" \
-H "Authorization: Bearer $TOKEN" \
-o nis2_compliance_report_h1_2026.pdf
period_end without period_start is rejected (400). Omitting
period_end with period_start set defaults the end to now.
The CVE posture is always a live snapshot — not retroactive
Unlike the decisions-history section, the CVE exposure/SLA posture
(unpatched CVE counts, mean days exposed, SLA breaches) is always
computed at generation time, never reconstructed for a past period.
There is no time-series store for this data — inventory_cve is a
snapshot replaced on every scan. A report generated today with
period_start=2026-01-01 shows today's posture alongside January's
decisions; it is not a picture of what the posture looked like in
January. The report states this explicitly rather than implying a
retroactive reconstruction that doesn't exist.
4. Scope by machine (client IDs and/or tags)¶
client_ids and tags restrict which machines the asset-scoped sections
cover — the union of both, same semantics as
POST /install/jobs tag targeting.
curl -s "http://localhost:8000/api/v1/compliance/nis2-report.pdf?tags=rhel9-prod,pci-scope" \
-H "Authorization: Bearer $TOKEN" \
-o nis2_compliance_report_pci-scope.pdf
curl -s "http://localhost:8000/api/v1/compliance/nis2-report.pdf?client_ids=3f2b1a90-...,7c8d9e10-..." \
-H "Authorization: Bearer $TOKEN" \
-o nis2_compliance_report_two-machines.pdf
If client_ids and/or tags are given but resolve to zero machines, the
endpoint returns 422 rather than silently generating a near-empty report.
For a user whose own machine access is already restricted, the requested
scope is intersected with what they can see (never widened) — if that
intersection is empty, it is also 422, not a report scoped to nothing.
Combine period and machine scoping freely:
curl -s "http://localhost:8000/api/v1/compliance/nis2-report.pdf?period_start=2026-01-01&tags=rhel9-prod" \
-H "Authorization: Bearer $TOKEN" \
-o nis2_compliance_report_rhel9-prod_2026.pdf
5. Reading the [À VALIDER: …] placeholder blocks¶
The report template (backend/templates/reports/nis2_report.html.j2) is
version-controlled, not editable from the UI — a compliance document needs
GRC sign-off before any content change, unlike Repod's other,
UI-customizable email templates.
Some sections of a NIS2 Article 21 report require regulatory judgment that cannot be derived from application data alone — organizational measures, incident-response procedures, supplier risk assessments, and similar narrative content Repod has no visibility into. Rather than invent plausible legal text, those sections render as an explicit placeholder:
What this means for you: any section carrying this marker is not
finished — it is a labeled gap for your compliance/GRC team to fill in
before the document is used as an official audit artifact. Do not remove
the marker by writing something in its place casually; do not send the PDF
to an external auditor with unresolved [À VALIDER: …] blocks still
present, since they signal "not yet reviewed," not "not applicable."
Everything else in the report (CVE posture metrics, SLA compliance figures, the decisions history) is derived directly and factually from Repod's own data — no interpretation is layered on top of it.
Verify it worked¶
- The download completed and the PDF opens without error.
- The header/cover section shows the correct generation timestamp and, if you scoped it, the correct asset scope (tags/client count) and decision period.
- The CVE posture figures match what
GET /compliance/nis2-posturereturns for the same scope right now: - If you set
period_start/period_end, confirm the decisions-history section only lists decisions inside that window — and confirm you did not interpret the CVE posture section as being from that period too. - Search the rendered PDF text for
À VALIDER— note every section still flagged, and route the document to your compliance/GRC reviewer before treating it as a finished, shareable audit artifact.