[DOCS] Pagina documentazione Testing & Simulazione

Pagina HTML standalone che descrive:
- Test Runner (L1-L6, SSE streaming, token auth, comandi speciali)
- Simulazione Demo (6 scenari SIM-01→06, 3 aziende, architettura)
- Worker Feedback AI (cron, flow, configurazione)
- URL di accesso, credenziali, reset dati demo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile 2026-03-10 09:40:27 +01:00
parent fc4fbda732
commit ca8f077a7a

View File

@ -0,0 +1,789 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NIS2 Agile — Sistema di Testing & Simulazione</title>
<style>
:root {
--primary: #06b6d4;
--green: #22c55e;
--red: #ef4444;
--yellow: #f59e0b;
--purple: #8b5cf6;
--orange: #f97316;
--dark: #0f172a;
--card: #1e293b;
--border: #334155;
--text: #e2e8f0;
--muted: #94a3b8;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--dark);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
line-height: 1.6;
}
/* ── Header ── */
.header {
background: linear-gradient(135deg, #0c4a6e 0%, #0e7490 100%);
padding: 48px;
border-bottom: 1px solid #0e7490;
}
.header-inner { max-width: 1100px; margin: 0 auto; }
.header h1 { font-size: 2rem; font-weight: 800; color: #fff; margin-bottom: 8px; }
.header p { color: #7dd3fc; font-size: 1rem; max-width: 640px; }
.badge-row { margin-top: 20px; display: flex; flex-wrap: wrap; gap: 8px; }
.badge {
display: inline-block; padding: 4px 14px; border-radius: 20px;
font-size: 0.75rem; font-weight: 700;
}
.badge-cyan { background: rgba(6,182,212,.2); color: #67e8f9; border: 1px solid rgba(6,182,212,.3); }
.badge-green { background: rgba(34,197,94,.2); color: #86efac; border: 1px solid rgba(34,197,94,.3); }
.badge-purple { background: rgba(139,92,246,.2); color: #c4b5fd; border: 1px solid rgba(139,92,246,.3); }
/* ── Nav ── */
.nav {
background: #1e293b;
border-bottom: 1px solid var(--border);
position: sticky; top: 0; z-index: 100;
}
.nav-inner {
max-width: 1100px; margin: 0 auto;
display: flex; gap: 0; overflow-x: auto;
}
.nav a {
padding: 14px 20px; font-size: 0.875rem; font-weight: 600;
color: var(--muted); text-decoration: none; white-space: nowrap;
border-bottom: 2px solid transparent; transition: all .2s;
}
.nav a:hover { color: var(--text); background: rgba(255,255,255,.03); }
.nav a.active { color: var(--primary); border-bottom-color: var(--primary); }
/* ── Layout ── */
.main { max-width: 1100px; margin: 0 auto; padding: 48px 24px; }
section { margin-bottom: 64px; }
h2 {
font-size: 1.375rem; font-weight: 800; color: #fff;
margin-bottom: 24px; padding-bottom: 12px;
border-bottom: 1px solid var(--border);
display: flex; align-items: center; gap: 10px;
}
h2 .icon { font-size: 1.25rem; }
h3 { font-size: 1rem; font-weight: 700; color: #cbd5e1; margin-bottom: 12px; margin-top: 28px; }
p { color: #94a3b8; margin-bottom: 12px; }
/* ── Cards ── */
.card {
background: var(--card); border: 1px solid var(--border);
border-radius: 12px; padding: 24px; margin-bottom: 16px;
}
.card-title { font-size: 0.9375rem; font-weight: 700; color: #fff; margin-bottom: 6px; }
.card p { margin-bottom: 0; }
/* ── Grid ── */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
@media (max-width: 768px) {
.grid-2, .grid-3 { grid-template-columns: 1fr; }
.header { padding: 32px 24px; }
.header h1 { font-size: 1.5rem; }
}
/* ── Level badges ── */
.level-badge {
display: inline-block; padding: 2px 10px; border-radius: 6px;
font-size: 0.7rem; font-weight: 800; font-family: monospace;
margin-right: 8px; vertical-align: middle;
}
.lv-l1 { background: rgba(6,182,212,.15); color: #67e8f9; border: 1px solid rgba(6,182,212,.3); }
.lv-l2 { background: rgba(139,92,246,.15); color: #c4b5fd; border: 1px solid rgba(139,92,246,.3); }
.lv-l3 { background: rgba(34,197,94,.15); color: #86efac; border: 1px solid rgba(34,197,94,.3); }
.lv-l4 { background: rgba(249,115,22,.15); color: #fdba74; border: 1px solid rgba(249,115,22,.3); }
.lv-l5 { background: rgba(245,158,11,.15); color: #fcd34d; border: 1px solid rgba(245,158,11,.3); }
.lv-l6 { background: rgba(236,72,153,.15); color: #f9a8d4; border: 1px solid rgba(236,72,153,.3); }
.lv-sim { background: rgba(239,68,68,.15); color: #fca5a5; border: 1px solid rgba(239,68,68,.3); }
.lv-infra { background: rgba(148,163,184,.15); color: #cbd5e1; border: 1px solid rgba(148,163,184,.3); }
/* ── Test table ── */
.test-table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
.test-table th {
text-align: left; padding: 10px 14px; font-size: 0.75rem; font-weight: 700;
color: var(--muted); text-transform: uppercase; letter-spacing: .05em;
border-bottom: 1px solid var(--border); background: rgba(255,255,255,.02);
}
.test-table td { padding: 10px 14px; border-bottom: 1px solid rgba(51,65,85,.5); color: #cbd5e1; }
.test-table tr:last-child td { border-bottom: none; }
.test-table tr:hover td { background: rgba(255,255,255,.02); }
/* ── Scenario cards ── */
.sim-card {
background: var(--card); border: 1px solid var(--border);
border-radius: 10px; padding: 20px; position: relative;
overflow: hidden;
}
.sim-card::before {
content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px;
}
.sim-card.cyan::before { background: linear-gradient(90deg, #06b6d4, #0891b2); }
.sim-card.orange::before { background: linear-gradient(90deg, #f97316, #ea580c); }
.sim-card.red::before { background: linear-gradient(90deg, #ef4444, #dc2626); }
.sim-card.purple::before { background: linear-gradient(90deg, #8b5cf6, #7c3aed); }
.sim-card.green::before { background: linear-gradient(90deg, #22c55e, #16a34a); }
.sim-card.yellow::before { background: linear-gradient(90deg, #f59e0b, #d97706); }
.sim-num { font-size: 0.7rem; font-weight: 800; color: var(--muted); margin-bottom: 6px; font-family: monospace; }
.sim-title { font-size: 0.9375rem; font-weight: 700; color: #fff; margin-bottom: 6px; }
.sim-desc { font-size: 0.8125rem; color: var(--muted); line-height: 1.5; }
.sim-steps { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.sim-step { font-size: 0.75rem; color: #64748b; display: flex; gap: 8px; margin-bottom: 4px; }
.sim-step span { color: #94a3b8; }
/* ── Company pills ── */
.company-pill {
display: inline-flex; align-items: center; gap: 6px;
padding: 4px 12px; border-radius: 20px; font-size: 0.75rem; font-weight: 600;
border: 1px solid var(--border); background: rgba(255,255,255,.04);
margin: 3px;
}
.dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
/* ── Code block ── */
.code-block {
background: #0f172a; border: 1px solid var(--border);
border-radius: 8px; padding: 16px 20px; font-family: 'Fira Code', 'Cascadia Code', monospace;
font-size: 0.8125rem; color: #93c5fd; overflow-x: auto;
margin: 12px 0;
}
.code-block .comment { color: #475569; }
.code-block .keyword { color: #67e8f9; }
.code-block .string { color: #86efac; }
.code-block .url { color: #fcd34d; }
/* ── Alert ── */
.alert {
padding: 14px 18px; border-radius: 8px; font-size: 0.875rem;
display: flex; gap: 12px; align-items: flex-start; margin-bottom: 16px;
}
.alert-info { background: rgba(6,182,212,.08); border: 1px solid rgba(6,182,212,.2); color: #7dd3fc; }
.alert-warning { background: rgba(245,158,11,.08); border: 1px solid rgba(245,158,11,.2); color: #fcd34d; }
.alert-success { background: rgba(34,197,94,.08); border: 1px solid rgba(34,197,94,.2); color: #86efac; }
/* ── URL button ── */
.url-btn {
display: inline-flex; align-items: center; gap: 8px;
background: rgba(6,182,212,.1); border: 1px solid rgba(6,182,212,.3);
color: #67e8f9; padding: 10px 18px; border-radius: 8px;
text-decoration: none; font-size: 0.875rem; font-weight: 600;
transition: all .2s; margin-right: 8px; margin-bottom: 8px;
}
.url-btn:hover { background: rgba(6,182,212,.2); border-color: #06b6d4; }
.url-btn-red {
background: rgba(239,68,68,.1); border-color: rgba(239,68,68,.3); color: #fca5a5;
}
.url-btn-red:hover { background: rgba(239,68,68,.2); }
/* ── Stats strip ── */
.stats-strip { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 24px; }
.stat-box {
background: var(--card); border: 1px solid var(--border);
border-radius: 10px; padding: 16px 24px; text-align: center; flex: 1; min-width: 100px;
}
.stat-num { font-size: 2rem; font-weight: 800; }
.stat-lbl { font-size: 0.75rem; color: var(--muted); margin-top: 2px; }
/* ── Worker box ── */
.worker-flow {
display: flex; align-items: center; flex-wrap: wrap; gap: 0;
background: #0f172a; border: 1px solid var(--border);
border-radius: 10px; overflow: hidden; margin: 16px 0;
}
.wf-step {
padding: 14px 18px; font-size: 0.8125rem; flex: 1; min-width: 120px;
text-align: center; border-right: 1px solid var(--border);
}
.wf-step:last-child { border-right: none; }
.wf-step .wf-icon { font-size: 1.25rem; display: block; margin-bottom: 4px; }
.wf-step .wf-label { color: #cbd5e1; font-weight: 600; }
.wf-step .wf-sub { color: var(--muted); font-size: 0.725rem; margin-top: 2px; }
/* ── Credentials box ── */
.creds-box {
background: #0f172a; border: 1px solid var(--border);
border-radius: 8px; overflow: hidden;
}
.creds-row {
display: flex; gap: 0; border-bottom: 1px solid rgba(51,65,85,.5);
font-size: 0.8125rem;
}
.creds-row:last-child { border-bottom: none; }
.creds-key {
padding: 10px 16px; width: 200px; flex-shrink: 0;
color: var(--muted); font-weight: 600; border-right: 1px solid rgba(51,65,85,.5);
font-family: monospace; font-size: 0.75rem;
}
.creds-val {
padding: 10px 16px; color: #93c5fd; font-family: monospace; font-size: 0.75rem;
}
/* ── Footer ── */
footer {
background: #1e293b; border-top: 1px solid var(--border);
padding: 32px 48px; text-align: center; color: var(--muted); font-size: 0.875rem;
}
</style>
</head>
<body>
<!-- ── Header ── -->
<div class="header">
<div class="header-inner">
<div style="font-size:.8rem;color:#7dd3fc;margin-bottom:8px;font-weight:600;">
NIS2 Agile — Documentazione Tecnica
</div>
<h1>Sistema di Testing &amp; Simulazione</h1>
<p>Due strumenti integrati per verificare la correttezza della piattaforma e dimostrare scenari reali NIS2 in ambienti demo.</p>
<div class="badge-row">
<span class="badge badge-cyan">Test Runner v2 — 36+ test</span>
<span class="badge badge-green">Simulazione — 6 scenari</span>
<span class="badge badge-purple">3 aziende demo</span>
</div>
</div>
</div>
<!-- ── Nav ── -->
<nav class="nav">
<div class="nav-inner">
<a href="#panoramica" class="active">Panoramica</a>
<a href="#test-runner">Test Runner</a>
<a href="#simulazione">Simulazione</a>
<a href="#aziende-demo">Aziende Demo</a>
<a href="#worker">Worker Feedback</a>
<a href="#accesso">Accesso & URL</a>
<a href="#credenziali">Credenziali</a>
</div>
</nav>
<div class="main">
<!-- ── Panoramica ── -->
<section id="panoramica">
<h2><span class="icon">🧭</span> Panoramica</h2>
<p>NIS2 Agile dispone di due strumenti distinti ma complementari per la verifica della qualità e la dimostrazione delle funzionalità:</p>
<div class="grid-2">
<div class="card" style="border-color:rgba(6,182,212,.3);">
<div class="card-title" style="color:#67e8f9;">🧪 Test Runner</div>
<p>Suite di test automatizzati a 6 livelli che verifica ogni endpoint API con asserzioni precise. Accesso protetto da token, output streaming in tempo reale, reset dati integrato.</p>
<div style="margin-top:12px;">
<a href="https://nis2.agile.software/test-runner.php?t=Nis2Test2026" target="_blank" class="url-btn">Apri Test Runner →</a>
</div>
</div>
<div class="card" style="border-color:rgba(34,197,94,.3);">
<div class="card-title" style="color:#86efac;">🎬 Simulazione Demo</div>
<p>Costruisce dati realistici attraverso le API reali (nessun INSERT SQL diretto) simulando 3 aziende con settori e complessità diversi su 6 scenari NIS2 reali.</p>
<div style="margin-top:12px;">
<a href="https://nis2.agile.software/simulate.html" target="_blank" class="url-btn">Apri Simulatore →</a>
</div>
</div>
</div>
<div class="alert alert-info">
<span></span>
<div>I due sistemi sono progettati per lavorare in sequenza: prima si esegue la <strong>simulazione</strong> per creare i dati demo, poi il <strong>test runner</strong> per verificare che tutto funzioni correttamente. Il pulsante "Reset + Simula + Testa Tutto" li esegue entrambi in automatico.</div>
</div>
</section>
<!-- ── Test Runner ── -->
<section id="test-runner">
<h2><span class="icon">🧪</span> Test Runner</h2>
<p>Il Test Runner è un'applicazione PHP (<code>public/test-runner.php</code>) che espone una UI web dark-theme con streaming SSE dei risultati in tempo reale. I test sono organizzati in <strong>6 livelli funzionali</strong> più un livello infrastrutturale.</p>
<div class="stats-strip">
<div class="stat-box"><div class="stat-num" style="color:#67e8f9;">36+</div><div class="stat-lbl">Test totali</div></div>
<div class="stat-box"><div class="stat-num" style="color:#86efac;">6</div><div class="stat-lbl">Livelli</div></div>
<div class="stat-box"><div class="stat-num" style="color:#c4b5fd;">SSE</div><div class="stat-lbl">Output streaming</div></div>
<div class="stat-box"><div class="stat-num" style="color:#fcd34d;">JWT</div><div class="stat-lbl">Auth token-based</div></div>
</div>
<h3>Livelli di Test</h3>
<div class="card" style="padding:0; overflow:hidden;">
<table class="test-table">
<thead>
<tr>
<th>Livello</th>
<th>Nome</th>
<th>Cosa verifica</th>
<th>Dipendenze</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="level-badge lv-infra">INFRA</span></td>
<td>Health &amp; Smoke</td>
<td>API status, endpoint rapidi, connettività DB</td>
<td></td>
</tr>
<tr>
<td><span class="level-badge lv-l1">L1</span></td>
<td>Auth &amp; JWT</td>
<td>Login, register, JWT access/refresh, change-password, rate limiting</td>
<td></td>
</tr>
<tr>
<td><span class="level-badge lv-l2">L2</span></td>
<td>Multi-Tenant Isolation</td>
<td>Isolamento dati tra organizzazioni diverse, cross-org protection</td>
<td>SIM-01</td>
</tr>
<tr>
<td><span class="level-badge lv-l3">L3</span></td>
<td>Compliance Core</td>
<td>Assessment, rischi, incidenti, policy, supply chain, asset, training, NCR/CAPA</td>
<td>SIM-01</td>
</tr>
<tr>
<td><span class="level-badge lv-l4">L4</span></td>
<td>B2B &amp; Services API</td>
<td>X-API-Key auth, scopes (read:all, admin:licenses), invite lifecycle, webhook delivery</td>
<td>SIM-01, SIM-06</td>
</tr>
<tr>
<td><span class="level-badge lv-l5">L5</span></td>
<td>Export &amp; Reports</td>
<td>CSV export (rischi/incidenti/asset/controlli), report HTML, audit executive</td>
<td>SIM-01</td>
</tr>
<tr>
<td><span class="level-badge lv-l6">L6</span></td>
<td>AI Features</td>
<td>Cross-org portfolio analysis, AI classify incident, AI suggest risk, normative feed</td>
<td>SIM-01, consultant user</td>
</tr>
<tr>
<td><span class="level-badge lv-sim">SIM</span></td>
<td>Simulazioni</td>
<td>Esecuzione completa SIM-01→06 con output dettagliato</td>
<td>Demo users nel DB</td>
</tr>
</tbody>
</table>
</div>
<h3>Accesso e autenticazione</h3>
<p>Il Test Runner è protetto da un token di sessione configurato come costante PHP:</p>
<div class="code-block">
<span class="comment">// Primo accesso — passa il token come query string</span>
<span class="url">https://nis2.agile.software/test-runner.php?t=Nis2Test2026</span>
<span class="comment">// Sessioni successive — il token è memorizzato in $_SESSION</span>
<span class="url">https://nis2.agile.software/test-runner.php</span>
</div>
<h3>Output streaming (SSE)</h3>
<p>I test vengono eseguiti come comandi bash su server. I risultati vengono trasmessi in tempo reale tramite <strong>Server-Sent Events</strong> al browser. Ogni test mostra:</p>
<div class="grid-2">
<div class="card"><div class="card-title" style="color:#86efac;">✓ PASS</div><p>Il comando ha restituito exit code 0 e l'output atteso.</p></div>
<div class="card"><div class="card-title" style="color:#fca5a5;">✗ FAIL</div><p>Il comando ha restituito errore o output non corrispondente.</p></div>
<div class="card"><div class="card-title" style="color:#fcd34d;">⚠ SKIP</div><p>Dipendenza mancante (utente demo non creato, SIM non eseguita).</p></div>
<div class="card"><div class="card-title" style="color:#f97316;">⚡ WARN</div><p>Il test è passato ma con avvertimenti non bloccanti.</p></div>
</div>
<h3>Comandi speciali</h3>
<div class="card">
<table class="test-table">
<thead><tr><th>Comando</th><th>Funzione</th></tr></thead>
<tbody>
<tr><td><span class="level-badge lv-infra">Verifica Hash Chain</span></td><td>Esegue <code>simulate-nis2.php --sim=SIM05</code> per verificare integrità SHA-256 del audit trail</td></tr>
<tr><td><span class="level-badge lv-infra">Reset Dati Demo</span></td><td>Esegue <code>reset-demo.sql</code> — cancella org/utenti con id&gt;4 e email <code>%.demo%</code></td></tr>
<tr><td><span class="level-badge lv-infra">Full Suite L1→L6</span></td><td>Esegue tutti i 6 livelli in sequenza, con output cumulativo</td></tr>
<tr><td><span class="level-badge lv-infra">Reset + Simula + Testa</span></td><td>Pipeline completa: reset → SIM-01→06 → Full Suite L1→L6</td></tr>
<tr><td><span class="level-badge lv-infra">DB Stats</span></td><td>Conta record per tabella via endpoint JSON</td></tr>
</tbody>
</table>
</div>
</section>
<!-- ── Simulazione ── -->
<section id="simulazione">
<h2><span class="icon">🎬</span> Sistema di Simulazione</h2>
<p>Il simulatore (<code>simulate-nis2.php</code>) costruisce dati demo realistici <em>esclusivamente attraverso le API REST</em>, come farebbe un utente reale. Nessun INSERT SQL diretto: ogni operazione testa sia la logica di business che la persistenza dati.</p>
<div class="alert alert-warning">
<span>⚠️</span>
<div><strong>Idempotenza:</strong> se un'organizzazione demo esiste già (stessa P.IVA), lo step viene saltato silenziosamente (<code>SKIP idempotent</code>) senza errore. Per ripartire da zero usare prima il <strong>Reset Dati Demo</strong>.</div>
</div>
<h3>Utilizzo</h3>
<div class="code-block">
<span class="comment"># Via browser (SSE streaming con UI dark-theme)</span>
<span class="url">https://nis2.agile.software/simulate.html</span>
<span class="comment"># Via CLI sul server</span>
<span class="keyword">php</span> simulate-nis2.php <span class="comment"># tutti gli scenari</span>
<span class="keyword">php</span> simulate-nis2.php --sim=SIM02 <span class="comment"># solo SIM-02</span>
<span class="keyword">php</span> simulate-nis2.php --sim=SIM06 <span class="comment"># solo B2B (indipendente)</span>
<span class="comment"># Variabili d'ambiente opzionali</span>
<span class="keyword">NIS2_SIM</span>=SIM06 <span class="keyword">php</span> simulate-nis2.php <span class="comment"># alias env per --sim</span>
<span class="keyword">NIS2_API_BASE</span>=https://... <span class="keyword">php</span> simulate-nis2.php <span class="comment"># override URL API</span>
<span class="keyword">NIS2_DEMO_EMAIL</span>=test@... <span class="keyword">php</span> simulate-nis2.php <span class="comment"># redirect email demo</span>
</div>
<h3>I 6 Scenari</h3>
<div class="grid-2">
<div class="sim-card cyan">
<div class="sim-num">SIM-01</div>
<div class="sim-title">Onboarding + Gap Assessment</div>
<div class="sim-desc">Registra 3 aziende, completa l'onboarding multi-step, esegue il gap assessment completo con tutte le 80 domande Art.21 NIS2 per ogni organizzazione.</div>
<div class="sim-steps">
<div class="sim-step"><span>Registrazione + email welcome</span></div>
<div class="sim-step"><span>Setup org (settore, dipendenti, entity type)</span></div>
<div class="sim-step"><span>80 domande × 3 aziende (categorizzate per Art.21)</span></div>
<div class="sim-step"><span>Completamento assessment + score compliance</span></div>
</div>
</div>
<div class="sim-card orange">
<div class="sim-num">SIM-02</div>
<div class="sim-title">Incidente Ransomware Art.23</div>
<div class="sim-desc">Simula un attacco ransomware critico su DataCore S.r.l. con attivazione della timeline obbligatoria NIS2 (early warning 24h, notification 72h, final report 30d).</div>
<div class="sim-steps">
<div class="sim-step"><span>Creazione incidente severity=critical</span></div>
<div class="sim-step"><span>AI classify: categoria, suggerimenti, severity</span></div>
<div class="sim-step"><span>Early warning CSIRT (24h)</span></div>
<div class="sim-step"><span>Notification formale (72h) + final report (30d)</span></div>
</div>
</div>
<div class="sim-card red">
<div class="sim-num">SIM-03</div>
<div class="sim-title">Data Breach Supply Chain</div>
<div class="sim-desc">Fornitore IT di MedClinic compromesso: aggiunta fornitore critico, assessment sicurezza, attivazione incidente Art.23 parallelo con gestione supply chain.</div>
<div class="sim-steps">
<div class="sim-step"><span>Aggiunta fornitore risk=critical</span></div>
<div class="sim-step"><span>Security assessment fornitore</span></div>
<div class="sim-step"><span>Data breach incident con AI classification</span></div>
<div class="sim-step"><span>Risk register aggiornato automaticamente</span></div>
</div>
</div>
<div class="sim-card purple">
<div class="sim-num">SIM-04</div>
<div class="sim-title">Whistleblowing Anonimo SCADA</div>
<div class="sim-desc">Segnalazione anonima Art.32 NIS2 per accesso non autorizzato ai sistemi SCADA di EnerNet. Assegnazione investigatore, tracciamento timeline, chiusura con esito.</div>
<div class="sim-steps">
<div class="sim-step"><span>Submit anonimo (no auth, solo token tracking)</span></div>
<div class="sim-step"><span>Assegnazione a compliance manager</span></div>
<div class="sim-step"><span>Timeline investigazione (3 eventi)</span></div>
<div class="sim-step"><span>Chiusura con esito e verifica tracking anonimo</span></div>
</div>
</div>
<div class="sim-card green">
<div class="sim-num">SIM-05</div>
<div class="sim-title">Audit Trail Hash Chain</div>
<div class="sim-desc">Verifica l'integrità della catena SHA-256 dell'audit trail per tutte e 3 le organizzazioni. Rileva eventuali record manomessi e genera export certificato.</div>
<div class="sim-steps">
<div class="sim-step"><span>Fetch audit log con prev_hash + entry_hash</span></div>
<div class="sim-step"><span>Verifica catena SHA-256 record per record</span></div>
<div class="sim-step"><span>Report integrità: verified/tampered/gaps</span></div>
<div class="sim-step"><span>Export certificato JSON (con hash contenuto)</span></div>
</div>
</div>
<div class="sim-card yellow">
<div class="sim-num">SIM-06</div>
<div class="sim-title">B2B License Provisioning</div>
<div class="sim-desc">Provisioning automatico di una nuova organizzazione tramite invite token B2B. SSO federato, token exchange, creazione org + utente + API key in un'unica chiamata.</div>
<div class="sim-steps">
<div class="sim-step"><span>Creazione invite token (admin:licenses)</span></div>
<div class="sim-step"><span>POST /api/services/provision con X-Provision-Secret</span></div>
<div class="sim-step"><span>Org + user + api_key creati atomicamente</span></div>
<div class="sim-step"><span>Verifica scadenza licenza + SSO JWT exchange</span></div>
</div>
</div>
</div>
<h3>Architettura del simulatore</h3>
<p>Il simulatore mantiene uno <strong>stato globale</strong> <code>$S</code> durante l'esecuzione per tracciare JWT, org IDs e statistiche:</p>
<div class="code-block">
<span class="comment">// Stato globale simulazione (in-memory per sessione)</span>
$S = [
<span class="string">'jwt'</span> => [], <span class="comment">// ['email' => token]</span>
<span class="string">'orgs'</span> => [], <span class="comment">// ['slug' => ['id', 'name', 'jwt']]</span>
<span class="string">'users'</span> => [], <span class="comment">// ['email' => ['id', 'jwt']]</span>
<span class="string">'stats'</span> => [<span class="string">'pass'</span> => 0, <span class="string">'skip'</span> => 0, <span class="string">'fail'</span> => 0, <span class="string">'warn'</span> => 0],
];
</div>
</section>
<!-- ── Aziende Demo ── -->
<section id="aziende-demo">
<h2><span class="icon">🏢</span> Aziende Demo</h2>
<p>Le simulazioni creano 3 organizzazioni demo con caratteristiche diversificate per coprire i principali settori NIS2 e i diversi livelli di obblighi normativi:</p>
<div class="grid-3">
<div class="card" style="border-color:rgba(6,182,212,.3);">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">
<div class="dot" style="background:#06b6d4;width:12px;height:12px;"></div>
<div class="card-title" style="color:#67e8f9;">DataCore S.r.l.</div>
</div>
<div style="font-size:0.8rem;color:#94a3b8;line-height:1.8;">
<div><strong style="color:#cbd5e1;">Settore:</strong> IT / Cloud &amp; Data Center</div>
<div><strong style="color:#cbd5e1;">Tipo:</strong> Essential Entity</div>
<div><strong style="color:#cbd5e1;">Dipendenti:</strong> 320</div>
<div><strong style="color:#cbd5e1;">Sede:</strong> Milano</div>
<div><strong style="color:#cbd5e1;">Scenari:</strong> SIM-01, SIM-02 (Ransomware)</div>
</div>
</div>
<div class="card" style="border-color:rgba(139,92,246,.3);">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">
<div class="dot" style="background:#8b5cf6;width:12px;height:12px;"></div>
<div class="card-title" style="color:#c4b5fd;">MedClinic Italia S.p.A.</div>
</div>
<div style="font-size:0.8rem;color:#94a3b8;line-height:1.8;">
<div><strong style="color:#cbd5e1;">Settore:</strong> Sanità</div>
<div><strong style="color:#cbd5e1;">Tipo:</strong> Important Entity</div>
<div><strong style="color:#cbd5e1;">Dipendenti:</strong> 750</div>
<div><strong style="color:#cbd5e1;">Sede:</strong> Roma</div>
<div><strong style="color:#cbd5e1;">Scenari:</strong> SIM-01, SIM-03 (Supply Chain)</div>
</div>
</div>
<div class="card" style="border-color:rgba(34,197,94,.3);">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">
<div class="dot" style="background:#22c55e;width:12px;height:12px;"></div>
<div class="card-title" style="color:#86efac;">EnerNet Distribuzione S.r.l.</div>
</div>
<div style="font-size:0.8rem;color:#94a3b8;line-height:1.8;">
<div><strong style="color:#cbd5e1;">Settore:</strong> Energia</div>
<div><strong style="color:#cbd5e1;">Tipo:</strong> Essential / Critical</div>
<div><strong style="color:#cbd5e1;">Dipendenti:</strong> 1.800</div>
<div><strong style="color:#cbd5e1;">Sede:</strong> Torino</div>
<div><strong style="color:#cbd5e1;">Scenari:</strong> SIM-01, SIM-04 (SCADA)</div>
</div>
</div>
</div>
<div class="alert alert-success">
<span></span>
<div>Tutti gli utenti demo usano la password <strong>NIS2Demo2026!</strong>. Le email vengono reindirizzate all'indirizzo configurato in <code>NIS2_DEMO_EMAIL</code> (default: <code>demo@nis2agile.it</code>).</div>
</div>
</section>
<!-- ── Worker Feedback ── -->
<section id="worker">
<h2><span class="icon">🤖</span> Worker Feedback AI</h2>
<p>Oltre ai test e alle simulazioni, la piattaforma include un <strong>worker autonomo</strong> per la risoluzione automatica delle segnalazioni utente tramite Claude AI.</p>
<div class="worker-flow">
<div class="wf-step">
<span class="wf-icon">🕐</span>
<div class="wf-label">Ogni 30 min</div>
<div class="wf-sub">cron root</div>
</div>
<div class="wf-step">
<span class="wf-icon">🔍</span>
<div class="wf-label">Fetch ticket</div>
<div class="wf-sub">status=in_lavorazione</div>
</div>
<div class="wf-step">
<span class="wf-icon">🤖</span>
<div class="wf-label">Claude Code CLI</div>
<div class="wf-sub">docker exec nis2-agile-devenv</div>
</div>
<div class="wf-step">
<span class="wf-icon"></span>
<div class="wf-label">POST /resolve</div>
<div class="wf-sub">con password gate</div>
</div>
<div class="wf-step">
<span class="wf-icon">📧</span>
<div class="wf-label">Email broadcast</div>
<div class="wf-sub">tutti i membri org</div>
</div>
</div>
<h3>Configurazione</h3>
<div class="creds-box">
<div class="creds-row">
<div class="creds-key">Script</div>
<div class="creds-val">scripts/feedback-worker.php</div>
</div>
<div class="creds-row">
<div class="creds-key">Crontab</div>
<div class="creds-val">*/30 * * * * /usr/bin/php8.4 /var/www/nis2-agile/scripts/feedback-worker.php</div>
</div>
<div class="creds-row">
<div class="creds-key">Log</div>
<div class="creds-val">/var/log/nis2/feedback-worker.log</div>
</div>
<div class="creds-row">
<div class="creds-key">Account worker</div>
<div class="creds-val">worker@nis2.agile.software (super_admin, dedicato)</div>
</div>
</div>
<h3>Test del worker</h3>
<div class="code-block">
<span class="comment"># Run manuale (Hetzner SSH)</span>
<span class="keyword">php8.4</span> /var/www/nis2-agile/scripts/feedback-worker.php
<span class="comment"># Verifica log</span>
<span class="keyword">tail</span> -f /var/log/nis2/feedback-worker.log
<span class="comment"># Output atteso (nessun ticket)</span>
[2026-03-10 09:16:27] === Feedback Worker avviato ===
[2026-03-10 09:16:27] Nessun ticket in lavorazione. Worker terminato.
</div>
</section>
<!-- ── Accesso ── -->
<section id="accesso">
<h2><span class="icon">🔗</span> URL di Accesso</h2>
<div class="grid-2">
<div>
<h3>Produzione</h3>
<a href="https://nis2.agile.software/simulate.html" target="_blank" class="url-btn">
🎬 Simulatore Demo
</a>
<a href="https://nis2.agile.software/test-runner.php?t=Nis2Test2026" target="_blank" class="url-btn">
🧪 Test Runner
</a>
<a href="https://nis2.agile.software/api-status.php" target="_blank" class="url-btn">
❤️ Health Check
</a>
</div>
<div>
<h3>DevEnv (container locale)</h3>
<a href="https://certisource.it/dev-nis2-api/" target="_blank" class="url-btn">
🔧 Dev API
</a>
<a href="https://certisource.it/dev-nis2-ide/" target="_blank" class="url-btn">
💻 Dev IDE
</a>
<a href="https://certisource.it/dev-nis2-browser/" target="_blank" class="url-btn">
🌐 Dev Browser
</a>
</div>
</div>
<h3>Reset dati demo</h3>
<div class="alert alert-warning">
<span>⚠️</span>
<div>Il reset cancella tutte le organizzazioni e gli utenti con <code>id &gt; 4</code> e email che contengono <code>.demo</code>. Le 4 org base e i super admin vengono preservati.</div>
</div>
<div class="code-block">
<span class="comment"># Via Test Runner UI → tasto "Reset Dati Demo"</span>
<span class="comment"># Via SSH (Hetzner)</span>
<span class="keyword">mysql</span> -u nis2_user -p nis2_agile_db &lt; /var/www/nis2-agile/docs/sql/reset-demo.sql
</div>
</section>
<!-- ── Credenziali ── -->
<section id="credenziali">
<h2><span class="icon">🔑</span> Credenziali</h2>
<h3>Test Runner</h3>
<div class="creds-box">
<div class="creds-row">
<div class="creds-key">Token accesso</div>
<div class="creds-val">Nis2Test2026</div>
</div>
<div class="creds-row">
<div class="creds-key">URL</div>
<div class="creds-val">https://nis2.agile.software/test-runner.php?t=Nis2Test2026</div>
</div>
</div>
<h3>Utenti demo (creati da SIM-01)</h3>
<div class="creds-box">
<div class="creds-row">
<div class="creds-key">Password comune</div>
<div class="creds-val">NIS2Demo2026!</div>
</div>
<div class="creds-row">
<div class="creds-key">DataCore admin</div>
<div class="creds-val">admin@datacore-srl.demo</div>
</div>
<div class="creds-row">
<div class="creds-key">MedClinic admin</div>
<div class="creds-val">admin@medclinic.demo</div>
</div>
<div class="creds-row">
<div class="creds-key">EnerNet admin</div>
<div class="creds-val">admin@enernet.demo</div>
</div>
<div class="creds-row">
<div class="creds-key">Consultant</div>
<div class="creds-val">consultant@nis2agile.demo</div>
</div>
</div>
<h3>API Key demo (DataCore — read:all)</h3>
<div class="creds-box">
<div class="creds-row">
<div class="creds-key">Chiave</div>
<div class="creds-val">nis2_152c1d87f8e6613d18a0510fd907c082</div>
</div>
<div class="creds-row">
<div class="creds-key">Scope</div>
<div class="creds-val">read:all</div>
</div>
<div class="creds-row">
<div class="creds-key">Header</div>
<div class="creds-val">X-API-Key: nis2_152c1d87f8e6613d18a0510fd907c082</div>
</div>
</div>
<h3>Sistema Feedback</h3>
<div class="creds-box">
<div class="creds-row">
<div class="creds-key">Password resolve</div>
<div class="creds-val">Nis2Feedback2026!</div>
</div>
<div class="creds-row">
<div class="creds-key">Worker account</div>
<div class="creds-val">worker@nis2.agile.software</div>
</div>
</div>
<div class="alert alert-warning" style="margin-top:24px;">
<span>🔒</span>
<div>Queste credenziali sono per uso esclusivo in ambiente di test/demo. Non usarle in produzione reale. La pagina non è indicizzata dai motori di ricerca.</div>
</div>
</section>
</div>
<footer>
NIS2 Agile — Documentazione Testing &amp; Simulazione · Aggiornata 2026-03-10 ·
<a href="https://nis2.agile.software/" style="color:var(--primary);">nis2.agile.software</a>
</footer>
<script>
// Highlight nav link on scroll
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('.nav a');
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
navLinks.forEach(a => a.classList.remove('active'));
const active = document.querySelector(`.nav a[href="#${entry.target.id}"]`);
if (active) active.classList.add('active');
}
});
}, { threshold: 0.3 });
sections.forEach(s => observer.observe(s));
</script>
</body>
</html>