DEV Community

Cover image for NIS2 Compliance 2025: What’s Actually In Force
Pentest Testing Corp
Pentest Testing Corp

Posted on

NIS2 Compliance 2025: What’s Actually In Force

TL;DR (for busy builders)

  • As of October 2025, NIS2 implementation varies by country—several EU/EEA states have enacted national laws, while others are still finalizing. Don’t rely on the directive text alone; align to each country’s law and guidance.
  • If you’re in financial services, DORA has applied since 17 January 2025; use it to drive operational resilience, incident reporting, and third-party oversight in parallel with NIS2 compliance.
  • Expect stricter incident-reporting clocks (e.g., initial notice within hours, follow-ups within days); build repeatable playbooks and evidence pipelines now.

NIS2 Compliance 2025: What’s Actually In Force


Status snapshot: what’s actually in force (Oct 2025)

  • Deadline was 17 Oct 2024. Multiple states (e.g., Belgium, Denmark, Greece, Hungary, Italy, Malta, Slovakia) have enacted national NIS2 laws; others (e.g., Germany, France) remain in process, creating cross-border variance for multi-country entities.
  • The Commission issued reasoned opinions in 2025 to lagging Member States, underscoring the uneven pace and the risk of infringement actions. Track country status continuously.
  • For day-to-day tracking, use a live transposition map (internal legal counsel should keep a canonical source and change log).

What that means for NIS2 compliance 2025: you must implement country-specific control deltas (not just the Directive text). Procurement, logging, incident thresholds, board accountability and fines may differ.


What changed under NIS2 (and why teams feel it)

  • Scope expanded: more sectors, including managed services and digital infrastructure providers.
  • Governance: board-level accountability, mandatory risk-management measures, oversight of third parties.
  • Reporting: faster notification timelines, structured follow-ups, and evidence expectations.
  • Cross-border: comply with each national law you operate under, plus supervisory authority expectations (which can add obligations beyond the baseline Directive).

90-day plan for NIS2 compliance in 2025 (with DORA alignment)

Days 1–14: Map scope & current posture

  1. Classify entities as Essential vs. Important per country; record the legal basis/article in a register.
  2. Inventory critical services and ICT assets, including third-party and intra-group providers.
  3. Baseline incident playbooks and reporting contacts by country (CSIRTs/competent authorities).
  4. If you’re in finance: map current DORA controls (ICT risk mgmt, testing, incident reporting, third-party oversight) to NIS2 gaps—create one joint backlog.

Days 15–45: Tighten detection, reporting & supplier risk

  1. Implement reporting timers (initial notice hours, 24–72h interim, 1-month final report) as SLAs in tooling.
  2. Refresh supplier reviews (onboarding, continuous monitoring, exit) with NIS2-aware clauses and KPIs.
  3. Run tabletop exercises focusing on cross-border incidents and regulator-grade summaries.

Days 46–90: Evidence, audits & resilience

  1. Centralize evidence: policies, control mappings, and logs that support reporting SLAs.
  2. Rehearse end-to-end incidents (prod + legal + PR) and produce the final regulator report draft.
  3. Operational resilience tie-in (DORA): scenario testing, ICT third-party concentration risk, and critical provider oversight—all mapped to NIS2 controls.

Practitioner tip: Auditors and regulators increasingly expect country-specific control deltas, traceable evidence, and repeatable reporting. Build once, templatize everywhere.


Free Website Vulnerability Scanner home

Screenshot of the free tools webpage where you can access security assessment tools.Screenshot of the free tools webpage where you can access security assessment tools.


Real-world, DEV-oriented implementation (copy-paste ready)

1) Country-specific control deltas (infra as data)

nis2-countries.yml

# Keep one source of truth for NIS2 control deltas per country
version: "2025-10-09"
countries:
  BE:
    law_ref: "Belgium NIS2 Act 2024..."
    incident:
      initial_hours: 24
      interim_hours: 72
      final_days: 30
    governance:
      board_training: true
      ciso_required: true
    supplier:
      critical_ict_third_party_register: true
  IT:
    law_ref: "Italy NIS2 decree..."
    incident:
      initial_hours: 24
      interim_hours: 72
      final_days: 30
    supplier:
      ict_third_party_contract_clauses:
        - "24x7 incident notice"
        - "Right-to-audit logs"
        - "Geofencing & data residency"
  MT:
    law_ref: "Malta NIS2 transposition 2025..."
    incident:
      initial_hours: 24
      interim_hours: 72
      final_days: 30
Enter fullscreen mode Exit fullscreen mode

generate-nis2-gap.py

#!/usr/bin/env python3
import yaml, json, sys, pathlib

cfg = yaml.safe_load(pathlib.Path("nis2-countries.yml").read_text())
target = sys.argv[1].upper() if len(sys.argv) > 1 else "BE"

# Minimal local baseline (org standard)
baseline = {
  "incident": {"initial_hours": 24, "interim_hours": 72, "final_days": 30},
  "governance": {"board_training": True, "ciso_required": False},
  "supplier": {"critical_ict_third_party_register": True}
}

country = cfg["countries"][target]
gaps = {}
for section, vals in baseline.items():
    cvals = country.get(section, {})
    for k, v in vals.items():
        if cvals.get(k) != v:
            gaps.setdefault(section, {})[k] = {"baseline": v, "country": cvals.get(k)}

print(json.dumps({
    "country": target,
    "law_ref": country["law_ref"],
    "gaps": gaps
}, indent=2))
Enter fullscreen mode Exit fullscreen mode

Usage:

python3 generate-nis2-gap.py MT > nis2-gap-MT.json
Enter fullscreen mode Exit fullscreen mode

2) Reporting SLAs as code (automated timers & evidence)

incident-sla.sql (PostgreSQL / Timescale)

CREATE TABLE IF NOT EXISTS incident_events (
  id SERIAL PRIMARY KEY,
  incident_id TEXT,
  phase TEXT CHECK (phase IN ('detected','classified','initial','interim','final')),
  ts TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Compute SLA deltas
WITH piv AS (
  SELECT incident_id,
         MIN(CASE WHEN phase='classified' THEN ts END) AS classified_at,
         MIN(CASE WHEN phase='initial' THEN ts END)    AS initial_at,
         MIN(CASE WHEN phase='interim' THEN ts END)    AS interim_at,
         MIN(CASE WHEN phase='final' THEN ts END)      AS final_at
  FROM incident_events GROUP BY incident_id
)
SELECT incident_id,
       EXTRACT(EPOCH FROM (initial_at - classified_at))/3600 AS h_initial_from_classified,
       EXTRACT(EPOCH FROM (interim_at - initial_at))/3600    AS h_interim_after_initial,
       EXTRACT(EPOCH FROM (final_at - initial_at))/24/3600   AS d_final_after_initial
FROM piv;
Enter fullscreen mode Exit fullscreen mode

Elastic (KQL) watch to auto-open an “Initial Report Needed” task when classification occurs but initial notice hasn’t been sent within your country SLA:

event.type: "incident-classified" and
now() - @timestamp > 24h and
not event.related: "initial-notice-sent"
Enter fullscreen mode Exit fullscreen mode

Set hours/days per country from nis2-countries.yml. These timers mirror DORA/NIS2 style clocks (e.g., hours for initial, days for final) to keep you audit-ready.


3) Regulator-grade incident summary (one-file template)

incident-final-report.md

# Final Incident Report (Country: {{COUNTRY}} / Law: {{LAW_REF}})
## Executive Summary
- Impacted services:
- Root cause:
- Dwell time:
- Data affected:

## Timeline (UTC)
- Detection:
- Classification:
- Initial Notification:
- Interim Updates:
- Final Containment:

## Technical Detail
- IOCs / TTPs:
- Control failures vs. policy:
- Post-incident hardening:

## Cross-Border Considerations
- Affected jurisdictions & authorities
- Country-specific obligations met (Y/N + evidence links)

## Evidence Appendix
- Log exports (hashes)
- Ticketing links
- Third-party notifications
Enter fullscreen mode Exit fullscreen mode

Generate with Jinja2:

from jinja2 import Template
import json, pathlib

data = json.loads(pathlib.Path("nis2-gap-MT.json").read_text())
tmpl = Template(pathlib.Path("incident-final-report.md").read_text())
print(tmpl.render(COUNTRY=data["country"], LAW_REF=data["law_ref"]))
Enter fullscreen mode Exit fullscreen mode

4) Supplier risk (DORA + NIS2) — contract & monitoring hooks

Supplier JSON profile (store in GRC or Git):

{
  "name": "Critical Cloud DB",
  "jurisdictions": ["DE","IT","MT"],
  "dora_critical": true,
  "notify_within_hours": 4,
  "right_to_audit": true,
  "evidence_feeds": ["syslog","api/status","soc2-report"],
  "exit_strategy": "30-day data export + runbook"
}
Enter fullscreen mode Exit fullscreen mode

Monitoring check (Node.js) to assert evidence feeds are alive:

import fetch from "node-fetch";

const feeds = ["https://vendor/api/status", "syslog://collector:514"];
// Pseudo-checks; wire to your stack
async function check() {
  const res = await fetch(feeds[0]);
  if (!res.ok) throw new Error("Vendor status API unhealthy");
  console.log("OK: vendor status");
  // Add syslog heartbeat checks, SIEM query, etc.
}
check().catch(e => { process.exitCode = 1; console.error(e); });
Enter fullscreen mode Exit fullscreen mode

5) Country-aware policy pack (Markdown-as-code)

policy/BE/incident.md

# Belgium — Incident Management (NIS2)
- Initial notification: within 24 hours of classification
- Interim: within 72 hours
- Final: within 30 days
Evidence:
- Ticketing export query
- SIEM saved search: `tag:"incident-initial-notice"`
- Signed board briefing PDF
Enter fullscreen mode Exit fullscreen mode

Keep one folder per country, generate PDFs on release, and attach hashes into your evidence index.


Sample Report from our tool to check Website Vulnerability

Sample vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.Sample vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.


Evidence & audits (what to keep, how to prove)

  • Policy/control deltas per country (YAML + Markdown shown above).
  • Centralized SLA metrics (SQL view + dashboard snapshots stored monthly).
  • Regulator-grade incident summaries (templated, reproducible, with hashed evidence).
  • Supplier artifacts (status API snapshots, log receipts, contract excerpts).

Auditors value traceability from law → control → evidence. Use a single repository and release tagging (e.g., nis2-2025.10) to lock evidence sets per quarter. Guidance from EU practitioners in 2025 emphasized these proof-oriented patterns.


Helpful internal links (to get it done fast)

Recent posts you may like


Paste-ready “Make it real” checklist

# 1) Create country deltas
mkdir -p policy/{BE,DE,FR,IT,MT}
cp templates/incident.md policy/BE/incident.md

# 2) Generate gap for Italy this quarter
python3 generate-nis2-gap.py IT > out/nis2-gap-IT.json

# 3) Run SLA dashboard extraction
psql -f incident-sla.sql
# (render Grafana → export PNG → store under evidence/2025Q4/)

# 4) Tabletop drill (cross-border)
./run_tabletop.sh --countries BE,DE,IT --clock "initial:24h,interim:72h,final:30d"

# 5) Tag & archive evidence
git tag -a nis2-2025.10 -m "Q4 evidence set"
Enter fullscreen mode Exit fullscreen mode

Final word

“NIS2 compliance 2025” isn’t a one-time project. Treat it like code: versioned, testable, country-aware—and tied to DORA where it applies. If you want a fast jumpstart, run the free scanner, then fold the findings into your Risk Assessment and Remediation workflows (links above).

Top comments (0)