nis2-agile/public/docs/testing-simulazione.html
DevEnv nis2-agile ca8f077a7a [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>
2026-03-10 09:40:27 +01:00

790 lines
41 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>