[FEAT] Services API, Webhook, Whistleblowing, Normative + integrazioni

Sprint completo — prodotto presentation-ready:

Services API (read-only, API Key + scope):
- GET /api/services/status|compliance-summary|risks-feed|incidents-feed
- GET /api/services/controls-status|assets-critical|suppliers-risk|policies-approved
- GET /api/services/openapi (spec OpenAPI 3.0.3 JSON)

Webhook Outbound (Stripe-like HMAC-SHA256):
- CRUD api_keys + webhook_subscriptions (Settings → 2 nuovi tab)
- WebhookService: retry 3x backoff (0s/5min/30min), delivery log
- Trigger auto in IncidentController, RiskController, PolicyController
- Delivery log, test ping, processRetry

Nuovi moduli:
- WhistleblowingController (Art.32 NIS2): anonimato garantito, timeline, token tracking
- NormativeController: feed NIS2/ACN/DORA con ACK tracciato per audit

Frontend:
- whistleblowing.html: form submit anonimo/firmato + gestione CISO
- normative.html: feed con presa visione documentata + progress bar ACK
- public/docs/api.html: documentazione API dark theme (Swagger-like)
- settings.html: tab API Keys + tab Webhook
- integrations/: guide per lg231, SustainAI, AllRisk, SIEM (widget + codice)
- Sidebar: Segnalazioni + Normative aggiunte a common.js

DB: migration 007 (api_keys, webhook_subscriptions, webhook_deliveries),
    008 (whistleblowing_reports + timeline),
    009 (normative_updates + normative_ack + seed NIS2/ACN/DORA/ISO)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-03-07 13:20:24 +01:00
co-authored by Claude Sonnet 4.6
parent 3f4b451e2a
commit 86e9bdded2
22 changed files with 5080 additions and 10 deletions
+156
View File
@@ -0,0 +1,156 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SustainAI × NIS2 Agile — Integrazione</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--primary: #06b6d4; --green: #10b981; --gray-200: #e2e8f0;
--gray-500: #64748b; --gray-700: #334155; --gray-900: #0f172a;
--radius: 8px; --font: -apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;
--mono: 'Cascadia Code','Consolas',monospace;
}
body { background: #f8fafc; font-family: var(--font); color: var(--gray-900); }
.header { background: linear-gradient(135deg, #064e3b, #065f46); padding: 40px 48px; color: #fff; }
.header-badges { display: flex; gap: 10px; margin-bottom: 16px; }
.badge { padding: 4px 12px; border-radius: 20px; font-size: 0.75rem; font-weight: 700; }
.badge-nis2 { background: rgba(6,182,212,0.2); color: #67e8f9; border: 1px solid rgba(6,182,212,0.3); }
.badge-sus { background: rgba(16,185,129,0.2); color: #6ee7b7; border: 1px solid rgba(16,185,129,0.3); }
h1 { font-size: 1.875rem; font-weight: 800; margin-bottom: 8px; }
.header p { color: #a7f3d0; font-size: 1rem; }
.container { max-width: 960px; margin: 0 auto; padding: 40px 24px; }
h2 { font-size: 1.25rem; font-weight: 700; margin-bottom: 12px; padding-bottom: 10px; border-bottom: 2px solid var(--gray-200); }
.section { margin-bottom: 48px; }
p { color: var(--gray-500); font-size: 0.9rem; line-height: 1.7; margin-bottom: 12px; }
pre { background: #1e293b; color: #e2e8f0; padding: 20px; border-radius: var(--radius); font-family: var(--mono); font-size: 0.8125rem; overflow-x: auto; line-height: 1.7; margin: 12px 0; }
code { background: #f1f5f9; padding: 2px 6px; border-radius: 4px; font-family: var(--mono); font-size: 0.85em; color: var(--gray-700); }
.step { display: flex; gap: 16px; margin-bottom: 20px; padding: 18px; background: #fff; border: 1px solid var(--gray-200); border-radius: var(--radius); }
.step-num { width: 30px; height: 30px; border-radius: 50%; background: var(--green); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.875rem; flex-shrink: 0; }
.info-box { padding: 14px 16px; border-radius: var(--radius); margin: 16px 0; font-size: 0.875rem; }
.info-green { background: #f0fdf4; border-left: 3px solid var(--green); color: #065f46; }
.mapping-table { width: 100%; border-collapse: collapse; }
.mapping-table th { padding: 10px 14px; background: #f1f5f9; font-size: 0.8rem; text-align: left; font-weight: 700; }
.mapping-table td { padding: 10px 14px; border-bottom: 1px solid var(--gray-200); font-size: 0.8375rem; }
.esg-badge { padding: 2px 8px; border-radius: 4px; font-size: 0.7rem; font-weight: 700; }
.esg-g { background: #e0f2fe; color: #075985; }
.esg-e { background: #dcfce7; color: #166534; }
.esg-s { background: #faf5ff; color: #6b21a8; }
</style>
</head>
<body>
<div class="header">
<div class="header-badges">
<span class="badge badge-sus">SustainAI</span>
<span>×</span>
<span class="badge badge-nis2">NIS2 Agile</span>
</div>
<h1>Integrazione SustainAI ← NIS2 Agile</h1>
<p>Alimenta l'area Governance (G) e Sociale (S) dei report ESG/sostenibilità con i dati di compliance cybersecurity NIS2. La governance della sicurezza informatica è un KPI ESG rilevante (GRI 418, SASB, CSRD).</p>
</div>
<div class="container">
<div class="section">
<h2>Mappatura NIS2 → ESG</h2>
<p>I dati NIS2 Agile si mappano naturalmente ai framework ESG più diffusi:</p>
<table class="mapping-table">
<thead><tr><th>Dato NIS2 Agile</th><th>Endpoint</th><th>Pilastro ESG</th><th>Framework</th></tr></thead>
<tbody>
<tr><td>Compliance score Art.21</td><td><code>/services/compliance-summary</code></td><td><span class="esg-badge esg-g">G</span> Governance</td><td>GRI 205, CSRD</td></tr>
<tr><td>Policy sicurezza approvate</td><td><code>/services/policies-approved</code></td><td><span class="esg-badge esg-g">G</span> Governance</td><td>ISO 27001, GRI 418</td></tr>
<tr><td>Incidenti data breach / Art.23</td><td><code>/services/incidents-feed</code></td><td><span class="esg-badge esg-s">S</span> Sociale</td><td>GRI 418 (Privacy)</td></tr>
<tr><td>Controlli di sicurezza implementati</td><td><code>/services/controls-status</code></td><td><span class="esg-badge esg-g">G</span> Governance</td><td>SASB</td></tr>
<tr><td>Rischio supply chain fornitori</td><td><code>/services/suppliers-risk</code></td><td><span class="esg-badge esg-e">E</span> Ambientale + <span class="esg-badge esg-g">G</span></td><td>GRI 308</td></tr>
<tr><td>Segnalazioni whistleblowing</td><td><code>/api/whistleblowing/stats</code></td><td><span class="esg-badge esg-s">S</span> Sociale</td><td>GRI 205 (Anti-corruzione)</td></tr>
</tbody>
</table>
</div>
<div class="section">
<h2>Step 1 — API Key con scope minimi</h2>
<p>Crea in NIS2 Agile una chiave con scope limitati per SustainAI:</p>
<pre>Scope richiesti:
read:compliance ← score e controlli Art.21
read:incidents ← incidenti per KPI privacy/GDPR
read:policies ← policy approvate (governance evidence)
read:supply_chain ← rischio fornitori ESG</pre>
</div>
<div class="section">
<h2>Step 2 — Sync mensile per report ESG</h2>
<pre><span style="color:#7dd3fc;">// SustainAI — sync_nis2_esg.php (cron mensile)</span>
<span style="color:#f1fa8c;">$apiKey</span> = getenv(<span style="color:#86efac;">'NIS2_API_KEY'</span>);
<span style="color:#f1fa8c;">$orgId</span> = getenv(<span style="color:#86efac;">'NIS2_ORG_ID'</span>);
<span style="color:#f1fa8c;">$base</span> = <span style="color:#86efac;">'https://nis2.certisource.it/api'</span>;
<span style="color:#f1fa8c;">$headers</span> = [
<span style="color:#86efac;">'X-API-Key: '</span> . <span style="color:#f1fa8c;">$apiKey</span>,
<span style="color:#86efac;">'X-Organization-Id: '</span> . <span style="color:#f1fa8c;">$orgId</span>,
];
<span style="color:#7dd3fc;">// G — Compliance score (KPI governance cybersecurity)</span>
<span style="color:#f1fa8c;">$compliance</span> = nis2Get(<span style="color:#f1fa8c;">$base</span> . <span style="color:#86efac;">'/services/compliance-summary'</span>, <span style="color:#f1fa8c;">$headers</span>);
<span style="color:#f1fa8c;">$cyberScore</span> = <span style="color:#f1fa8c;">$compliance</span>[<span style="color:#86efac;">'data'</span>][<span style="color:#86efac;">'overall_score'</span>] ?? 0;
<span style="color:#f1fa8c;">$policyCount</span> = <span style="color:#f1fa8c;">$compliance</span>[<span style="color:#86efac;">'data'</span>][<span style="color:#86efac;">'policies'</span>][<span style="color:#86efac;">'approved'</span>] ?? 0;
<span style="color:#7dd3fc;">// S — Privacy breaches (GRI 418)</span>
<span style="color:#f1fa8c;">$incidents</span> = nis2Get(<span style="color:#f1fa8c;">$base</span> . <span style="color:#86efac;">'/services/incidents-feed?significant_only=1'</span>, <span style="color:#f1fa8c;">$headers</span>);
<span style="color:#f1fa8c;">$breaches</span> = array_filter(
<span style="color:#f1fa8c;">$incidents</span>[<span style="color:#86efac;">'data'</span>][<span style="color:#86efac;">'incidents'</span>] ?? [],
fn(<span style="color:#f1fa8c;">$i</span>) => <span style="color:#f1fa8c;">$i</span>[<span style="color:#86efac;">'classification'</span>] === <span style="color:#86efac;">'data_breach'</span>
);
<span style="color:#7dd3fc;">// G — Supply chain risk (ESG fornitori)</span>
<span style="color:#f1fa8c;">$suppliers</span> = nis2Get(<span style="color:#f1fa8c;">$base</span> . <span style="color:#86efac;">'/services/suppliers-risk'</span>, <span style="color:#f1fa8c;">$headers</span>);
<span style="color:#f1fa8c;">$highRiskSuppliers</span> = <span style="color:#f1fa8c;">$suppliers</span>[<span style="color:#86efac;">'data'</span>][<span style="color:#86efac;">'stats'</span>][<span style="color:#86efac;">'high'</span>] +
<span style="color:#f1fa8c;">$suppliers</span>[<span style="color:#86efac;">'data'</span>][<span style="color:#86efac;">'stats'</span>][<span style="color:#86efac;">'critical'</span>];
<span style="color:#7dd3fc;">// Aggiorna KPI ESG in SustainAI</span>
EsgKpiService::updateCyberGovernance([
<span style="color:#86efac;">'nis2_score'</span> => <span style="color:#f1fa8c;">$cyberScore</span>,
<span style="color:#86efac;">'policies_approved'</span> => <span style="color:#f1fa8c;">$policyCount</span>,
<span style="color:#86efac;">'data_breaches'</span> => count(<span style="color:#f1fa8c;">$breaches</span>),
<span style="color:#86efac;">'high_risk_suppliers'</span> => <span style="color:#f1fa8c;">$highRiskSuppliers</span>,
<span style="color:#86efac;">'period'</span> => date(<span style="color:#86efac;">'Y-m'</span>),
]);</pre>
</div>
<div class="section">
<h2>Widget NIS2 per Report ESG SustainAI</h2>
<pre>&lt;!-- SustainAI: sezione Governance → Cybersecurity KPIs --&gt;
&lt;div id="nis2-esg-widget" style="padding:20px; border:1px solid #e2e8f0; border-radius:8px; background:#f0fdf4;"&gt;&lt;/div&gt;
&lt;script&gt;
fetch('https://nis2.certisource.it/api/services/compliance-summary', {
headers: { 'X-API-Key': '<span style="color:#86efac;">nis2_YOUR_KEY</span>', 'X-Organization-Id': '<span style="color:#86efac;">ORG_ID</span>' }
}).then(r => r.json()).then(({ data }) => {
document.getElementById('nis2-esg-widget').innerHTML = `
&lt;h4 style="font-size:.875rem; font-weight:700; color:#065f46; margin-bottom:16px;"&gt;
🔒 Governance Cybersecurity — NIS2 Compliance
&lt;/h4&gt;
&lt;div style="display:grid; grid-template-columns:repeat(4,1fr); gap:12px; text-align:center;"&gt;
&lt;div&gt;&lt;div style="font-size:1.5rem; font-weight:800; color:#06b6d4;"&gt;${data.overall_score}%&lt;/div&gt;
&lt;div style="font-size:.7rem; color:#64748b;"&gt;NIS2 Score&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div style="font-size:1.5rem; font-weight:800; color:#10b981;"&gt;${data.policies.approved}&lt;/div&gt;
&lt;div style="font-size:.7rem; color:#64748b;"&gt;Policy Approvate&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div style="font-size:1.5rem; font-weight:800; color:#f59e0b;"&gt;${data.risks.high}&lt;/div&gt;
&lt;div style="font-size:.7rem; color:#64748b;"&gt;Rischi HIGH&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div style="font-size:1.5rem; font-weight:800; color:#ef4444;"&gt;${data.incidents.significant}&lt;/div&gt;
&lt;div style="font-size:.7rem; color:#64748b;"&gt;Incidenti Art.23&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p style="font-size:.7rem; color:#94a3b8; margin-top:12px; text-align:right;"&gt;
Fonte: NIS2 Agile — nis2.certisource.it — Aggiornato: ${new Date().toLocaleDateString('it')}
&lt;/p&gt;`;
});
&lt;/script&gt;</pre>
</div>
<div class="info-box info-green">
<strong>CSRD / ESRS E5:</strong> La cybersecurity è esplicitamente inclusa nelle ESRS come rischio materiale (ESRS 2 IRO-1). NIS2 Agile fornisce le evidenze documentali per il reporting CSRD sul governo dei rischi digitali.
</div>
</div>
</body>
</html>