NIS2 Compliance-Officer End-to-End Scenarios

StatusDesign + live-stack Playwright authoring (QA / Test Automation)
OwnerTest Automation + NIS2 compliance specialist
PackNIS2 Evidence Pack — claim boundary operator-support
AudienceQA authors writing the live-stack specs, and product/compliance reviewers validating the operator-support framing

Purpose

This document captures realistic, end-to-end scenarios written from the point of view of a NIS2-obligated compliance officer who uses the Stella Ops NIS2 Evidence Pack to author a Statement of Applicability (SoA) control register, publish a signed SoA bundle, track Article 23 incident milestones, and review thirteen-area effectiveness evidence.

The companion live-stack Playwright specs author (do not run — a later wave executes them one by one):

Claim boundary (forbidden vs. approved copy)

Per docs/compliance/README.mdthe pack is operator-support: Stella helps the customer collect, review, and export evidence; the customer remains the regulated decision-maker. The scenarios and specs MUST NOT assert or imply “Stella is NIS2 compliant”. Approved framing is “NIS2 evidence support”.

Thematic areas 8 (cyber hygiene/training), 10 (HR security) and 13 (environmental/physical security) are not Stella-native telemetry domains. They must stay in the register with explicit applicability: not-applicable and reason not-applicable-to-stella-native-telemetry, backed by tenant-supplied evidence pointers. The authoring form defaults areas 8/10/13 to not-applicable with that reason (nis2-control-author-form.component.ts DEFAULT_NOT_APPLICABLE_AREAS).

System facts the scenarios rely on (verified against src/)

Console routes (Web)

Gateway-fronted API routes (what the live UI + request calls hit)

PurposeGateway pathService pathScope
List NIS2 registerGET /api/policy/control-register/nis2Policy …/api/policy/control-register/nis2policy:read
Author NIS2 controlPOST /api/policy/control-register/nis2Policypolicy:author
Archive NIS2 controlDELETE /api/policy/control-register/nis2/{controlId}Policypolicy:author
Framework projectionGET /api/policy/control-register/frameworks/nis2Policypolicy:read
SoA export profileGET /api/export-center/v1/exports/nis2/soa/profileExportCenter /v1/exports/nis2/soa/profileexport:read (ExportViewer)
Start SoA runPOST /api/export-center/v1/exports/nis2/soa/runsExportCenterexport:operate (ExportOperator)
Get SoA runGET /api/export-center/v1/exports/nis2/soa/runs/{runId}ExportCenterexport:read
Download SoA bundleGET /api/export-center/v1/exports/nis2/soa/runs/{runId}/bundleExportCenterexport:read
Assurance export profilesGET /api/export-center/v1/exports/assurance/profilesExportCenterexport:read
Article 23 timeline profileGET /api/v1/assurance/reporting-timeline-profiles/nis2.article23.incidentNotifynotify.viewer
Article 23 profile readinessGET /api/v1/assurance/reporting-timeline-profiles/nis2.article23.incident/readinessNotifynotify.viewer
Persisted incident timelinesGET /api/v1/regulatory/reporting-timelines?regime=nis2Notifynotify.viewer

Source of the gateway routing: src/Router/StellaOps.Gateway.WebService/appsettings.json (^/api/(cvss|gate|exceptions|policy)(.*) → policy-engine; ^/api/export-center/v1/exports… → exportcenter; ^/api/v1/assurance/reporting-timeline-profiles… → notify).

Deterministic hashes and identifiers

Readiness / fail-closed posture (important for assertions)

Scenario 1 — Author the SoA control register

Compliance-officer narrative. “I am preparing my organisation’s NIS2 Statement of Applicability. For each of the thirteen Article 21 thematic areas I record the controls we operate, who owns them, and where the evidence lives. Some controls are backed by Stella-native telemetry (gates, scans, evidence capsules); others — supplier attestations, HR/training, physical security — are tenant-supplied evidence I attach by reference. For areas 8, 10 and 13 I record an explicit not-applicable to Stella-native telemetry decision because Stella does not run my workforce or facilities.”

Flow.

  1. Sign in to Console (admin), open /assurance/nis2/control-register.
  2. Confirm the page shows Live Policy data (not contract-fixture) — chip + absence of the result.source === 'contract-fixture' warning. (If the stack has no register rows yet, authoring in this scenario creates them.)
  3. Author an applicable, Stella-native area-6 control (secure development gate) via the author form — nis2-author-control-button → form → nis2-author-submit.
  4. Author an applicable, tenant-supplied area-5 supplier-evidence control with a tenant-evidence://… pointer and submittedByRole.
  5. Author a not-applicable area-10 (HR security) control; assert the form pre-fills applicability=not-applicable + reason not-applicable-to-stella-native-telemetry.
  6. Reload the register; confirm each control appears with its mappingHash, area name, evidence mode, and audit event ids.

Concrete UI + API.

Expected assertions.

Scenario 2 — Publish a signed Statement of Applicability

Compliance-officer narrative. “Once my register is complete for all thirteen areas, I publish a signed, deterministic SoA bundle that my auditor (or the competent authority on handoff) can verify offline. The bundle pins the control register snapshot hash and the SoA export hash. If my signing trust roots or ExportCenter storage aren’t configured, the system must block me — it must never hand me a bundle that looks signed but isn’t.”

Flow.

  1. From the control register (live data), click Export SoA (nis2-export-soa-button).
  2. The Console fetches the profile (/exports/nis2/soa/profile), confirms status === "active", then POST …/soa/runs with a controlRegisterSnapshot built from the live register.
  3. The SoA panel (nis2-soa-export-status) shows the job/run ids, the bundle hash and SoA hash, progress, and a signed-bundle link.
  4. (API) Download the bundle from …/soa/runs/{runId}/bundle and assert the content type + that the run’s hashes are present and stable.

Concrete UI + API.

Expected assertions.

Scenario 3 — Article 23 incident reporting timeline

Compliance-officer narrative. “If we suffer a significant incident, NIS2 Article 23 puts me on a clock: an early warning within 24 hours, a full notification within 72 hours, and a final report within one month. Stella gives me a signed, replayable timeline whose source of truth is Notify, so I can prove to the authority exactly when each milestone was met.”

Flow.

  1. Open /assurance/nis2/incidents; confirm the page renders (nis2-incident-dashboard-page) and links back to the control register.
  2. (API) Fetch the profile …/reporting-timeline-profiles/nis2.article23.incident; assert the three milestone definitions and their due windows.
  3. (API) Fetch profile readiness …/nis2.article23.incident/readiness; assert stable reason codes and that no secret values are exposed.
  4. (API) Fetch persisted timelines …/regulatory/reporting-timelines?regime=nis2; for any present timeline, assert milestone names/states, deadline statuses, replay hash, and signed transmissions.

Concrete UI + API.

Expected assertions.

Scenario 4 — Thirteen-area effectiveness (KPI) dashboard

Compliance-officer narrative. “Between audits I watch a KPI dashboard that scores effectiveness across all thirteen thematic areas, so I can spot a control whose evidence is going stale before my auditor does.”

Flow.

  1. Open /assurance/nis2/effectiveness; confirm the page renders (nis2-effectiveness-dashboard-page).
  2. Assert per-area effectiveness cards render (nis2-effectiveness-area-{n}) and that the view links back to the control register and incidents.

Concrete UI + API.

Expected assertions.

How to run (later wave — do NOT run now)

The specs target the live Docker stack at https://stella-ops.local (ignoreHTTPSErrors) and carry their own live OIDC login + Bearer-token API context, modelled on the live-deployment specs that scripts/run-live-deployment-vm-e2e.mjs runs via --config.

Config note (verified): the existing live-Docker config playwright.e2e.config.ts has testDir: './e2e', so it does not discover specs placed under tests/e2e/compliance/ (Playwright filters positional spec paths against testDir). A dedicated live config was therefore added — playwright.nis2-compliance.config.ts (testDir tests/e2e/compliance, live baseURL, no webServer), mirroring playwright.airgap-federation.config.ts. npx playwright test --config playwright.nis2-compliance.config.ts --list discovers all 12 tests across the 4 files.

# from src/Web/StellaOps.Web, with the docker compose stack up at https://stella-ops.local
$env:STELLAOPS_ADMIN_USER = "admin"
$env:STELLAOPS_ADMIN_PASS = "<the password your installer printed>"
npx playwright test --config playwright.nis2-compliance.config.ts
# filter one scenario:
npx playwright test --config playwright.nis2-compliance.config.ts -g "@nis2-soa"

Scenario tags (on test.describe): @nis2-control-register, @nis2-soa, @nis2-article23, @nis2-effectiveness.

Known gaps (report only — do not fix)

  1. No HTTP path to open/advance an Article 23 timeline. …/regulatory/reporting-timelines is read-only; live timelines come from Notify’s incident pipeline or the (deprecated/soak) QA fixture-import endpoint /api/v1/qa/fixtures/advanced-assurance-golden/notify-import (and the newer stellaops-tester service). Scenario 3 therefore verifies the profile + readiness contract and asserts persisted-timeline shape when present; full milestone-driving needs the incident pipeline or fixture seed.
  2. SoA export depends on production signing config. Without Export:Nis2Soa:SigningKeyId / TrustRoots / StorageRoot the run fails closed. The local compose stack may not ship these, so the signed-bundle assertions are guarded (configured vs. fail-closed branch). Verifying the configured happy-path needs those options set in the deployment.
  3. Publish-snapshot + 13-area completeness gate UI is landing concurrently (SPRINT_20260530_020, same component dir). Its exact testids are not yet stable; Scenario 2’s completeness-gate assertion probes defensively and records the gate as pending if the control isn’t present, without touching the shared component files.
  4. Effectiveness KPI telemetry source (/api/telemetry/nis2/effectiveness) may be empty on a stack without seeded KPI data; the area-card assertions are guarded behind data presence.