# Integrazione lg231 Agile ↔ NIS2 Agile > **Documento per l'agente Claude di lg231.** > Leggi questo file prima di implementare qualsiasi integrazione con NIS2 Agile. --- ## Perché integrare lg231 gestisce compliance D.Lgs. 231/2001 (reati societari, OdV, MOG). NIS2 Agile gestisce compliance Direttiva NIS2 (cybersecurity, incidenti, rischi IT). I due mondi si sovrappongono su tre punti critici: | Evento NIS2 | Rilevanza 231 | |---|---| | Incidente cybersecurity (Art.23 NIS2) | Reato presupposto 231 art.24-bis (criminalità informatica) | | Fornitore IT ad alto rischio | Rischio 231: complicità in reati informatici via supply chain | | Policy sicurezza non approvata | Gap nel MOG (Modello Organizzativo e di Gestione) | | Score compliance NIS2 basso | Indicatore per l'OdV di carenza nei controlli | | Whistleblowing sicurezza | Potenziale escalation verso OdV 231 | --- ## Architettura dell'integrazione ``` lg231 Agile NIS2 Agile ──────────────────────────── ──────────────────────────── company-ms ServicesController.php companies.metadata JSON ───► /api/services/* (REST, X-API-Key) + nis2_api_key /api/webhooks/* (webhook outbound) + nis2_org_id ◄─── WebhookController.php + nis2_enabled (bool) webhook → POST lg231-endpoint ``` --- ## Cosa fa lg231 (lato mittente/ricevente) ### 1. Aggiungere NIS2 al provider-config (company-ms) **File**: `services/company-ms/public/index.php` Nel metodo `PATCH /companies/{id}/provider-config`, aggiungere ai `$allowed`: ```php $allowed = [ 'certisource_pat', 'certisource_pat_expires_at', 'anthropic_api_key', 'openai_api_key', // ── Nuovo provider NIS2 ── 'nis2_api_key', // chiave API generata in NIS2 Agile → Settings → API Keys 'nis2_org_id', // ID organizzazione NIS2 (intero, visibile in NIS2 dashboard URL) 'nis2_enabled', // bool: abilitare pull automatico dati NIS2 ]; ``` Nel metodo `GET /companies/{id}/provider-config`, aggiungere alla response: ```php $nis2Key = $meta['nis2_api_key'] ?? null; $nis2OrgId = $meta['nis2_org_id'] ?? null; $nis2Enabled = (bool) ($meta['nis2_enabled'] ?? false); // nella response array: 'nis2_configured' => !empty($nis2Key) && !empty($nis2OrgId), 'nis2_api_key' => $nis2Key, // uso interno microservizi 'nis2_org_id' => $nis2OrgId, 'nis2_enabled' => $nis2Enabled, 'nis2_key_hint' => !empty($nis2Key) ? 'nis2_...' . substr($nis2Key, -4) : null, ``` ### 2. Nuovo servizio NIS2Client (shared-lib o risk-ms) Creare `shared/nis2-lib/src/Nis2Client.php`: ```php class Nis2Client { const BASE_URL = 'https://nis2.agile.software/api/services'; public static function get( string $endpoint, string $apiKey, array $query = [], int $timeout = 10 ): array { $url = self::BASE_URL . $endpoint; if ($query) $url .= '?' . http_build_query($query); $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $timeout, CURLOPT_HTTPHEADER => [ 'X-API-Key: ' . $apiKey, 'Accept: application/json', 'X-Caller: lg231-agile/1.0', ], ]); $body = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($code !== 200 || !$body) { return ['success' => false, 'http_code' => $code, 'data' => null]; } $decoded = json_decode($body, true); return ['success' => true, 'http_code' => 200, 'data' => $decoded]; } } ``` --- ## API NIS2 disponibili **Base URL**: `https://nis2.agile.software/api/services` **Auth**: Header `X-API-Key: {nis2_api_key}` (la chiave è legata a una singola org NIS2) ### Endpoint GET disponibili | Endpoint | Scope richiesto | Cosa restituisce | |---|---|---| | `/status` | — (no auth) | Stato piattaforma, versione | | `/compliance-summary` | `read:compliance` | Score NIS2 (0-100) per dominio Art.21, rischi aperti, incidenti | | `/risks/feed` | `read:risks` | Lista rischi: id, title, level (low/medium/high/critical), status, area | | `/incidents/feed` | `read:incidents` | Incidenti: id, title, severity, status, notified_csirt, deadline_72h | | `/controls/status` | `read:compliance` | Stato controlli Art.21 per dominio (implemented/partial/missing) | | `/assets/critical` | `read:assets` | Asset critici con tipo, criticità, dipendenze | | `/suppliers/risk` | `read:suppliers` | Fornitori: nome, risk_level, ultimo_assessment, is_flagged | | `/policies/approved` | `read:policies` | Policy approvate: titolo, area, data approvazione, versione | **Header risposta sempre presenti**: - `X-NIS2-API-Version: 1.0` - `X-NIS2-Org-Id: {org_id}` - `X-RateLimit-Limit: 100` - `X-RateLimit-Remaining: N` **Rate limit**: 100 req/ora per API key. ### Esempio risposta `/compliance-summary` ```json { "success": true, "data": { "organization": { "id": 5, "name": "Azienda XYZ S.r.l.", "sector": "energia", "nis2_entity_type": "essential" }, "compliance": { "overall_score": 73, "label": "Parziale", "assessment_date": "2026-03-07T10:00:00+01:00", "domain_scores": [ { "domain": "Gestione del rischio", "score": 80, "status": "compliant" }, { "domain": "Sicurezza supply chain", "score": 45, "status": "partial" }, { "domain": "Gestione incidenti", "score": 90, "status": "compliant" } ] }, "risks": { "total": 12, "open": 8, "high_critical": 3, "mitigated": 4 }, "incidents": { "total": 2, "open": 1, "significant": 1, "overdue": 0 }, "generated_at": "2026-03-07T13:30:00+01:00" } } ``` ### Esempio risposta `/incidents/feed` ```json { "success": true, "data": { "incidents": [ { "id": 3, "title": "Ransomware server produzione", "severity": "critical", "status": "open", "is_significant": true, "notified_csirt": true, "deadline_72h": "2026-03-09T08:00:00+01:00", "overdue": false, "created_at": "2026-03-07T08:00:00+01:00" } ], "total": 1, "filters_applied": {} } } ``` --- ## Come creare una API Key in NIS2 1. Login su https://nis2.agile.software con l'account dell'azienda 2. Vai su **Settings → API Keys** 3. Click **"Nuova API Key"** → nome: "lg231 Integration" → scope: `read:all` 4. Copia la chiave (mostrata una sola volta, formato: `nis2_XXXX...`) 5. In lg231: PATCH `/companies/{id}/provider-config` con `{ "nis2_api_key": "nis2_...", "nis2_org_id": 5 }` --- ## Cosa mostrare in lg231 ### Widget "Cybersecurity NIS2" nella company view Chiamare `GET /compliance-summary` e mostrare: - Gauge score 0-100 con colori (verde ≥70, arancione 40-69, rosso <40) - Badge "Essential" / "Important" / "Volontario" - Numero rischi high/critical aperti - Numero incidenti aperti (con alert se `overdue: true`) - Link "Apri NIS2 Agile" → `https://nis2.agile.software/dashboard.html` ### Integrazione Risk Register (risk-ms) Quando lg231 crea un'analisi dei rischi per l'azienda, importare i rischi cyber NIS2: ```php // Nel risk-ms, durante la creazione di un nuovo risk assessment: $nis2Risks = Nis2Client::get('/risks/feed', $apiKey, ['level' => 'high,critical']); foreach (($nis2Risks['data']['risks'] ?? []) as $r) { // Suggerisci all'utente di includerlo nel registro 231 // Tipo: "Rischio cyber (da NIS2 Agile): " . $r['title'] // Categoria 231: art.24-bis (criminalità informatica) } ``` ### Escalation Incidente → OdV (monitoring-ms) Quando NIS2 segnala un incidente significativo (`is_significant: true`), creare attività OdV: ```php $incidents = Nis2Client::get('/incidents/feed', $apiKey, ['significant' => 1, 'status' => 'open']); foreach (($incidents['data']['incidents'] ?? []) as $inc) { // Crea odv_activity in monitoring-ms: // type: 'cyber_incident_notifica' // description: 'Incidente NIS2 significativo: ' . $inc['title'] // priority: 'high' // source: 'nis2_agile' // reference_id: $inc['id'] } ``` --- ## Webhook NIS2 → lg231 (facoltativo, avanzato) NIS2 può inviare notifiche push su eventi. lg231 deve esporre un endpoint ricevente. **Endpoint lg231 da creare**: `POST /api/integrations/nis2-webhook` **Firma**: header `X-NIS2-Signature: sha256=HMAC_HEX` Verificare la firma: ```php $secret = $company['metadata']['nis2_webhook_secret']; $body = file_get_contents('php://input'); $expected = 'sha256=' . hash_hmac('sha256', $body, $secret); $received = $_SERVER['HTTP_X_NIS2_SIGNATURE'] ?? ''; if (!hash_equals($expected, $received)) { return 401; } ``` **Eventi ricevuti**: - `incident.created` → crea alert in monitoring-ms - `incident.deadline_warning` → notifica urgente OdV - `risk.created_high` → aggiunge al risk register 231 - `compliance.score_changed` → aggiorna widget score - `supplier.risk_flagged` → flagga fornitore in supply chain 231 **Registrare la subscription in NIS2**: ``` POST https://nis2.agile.software/api/webhooks/subscriptions Authorization: Bearer {jwt} { "url": "https://lg231.agile.software/api/integrations/nis2-webhook", "events": ["incident.created", "incident.deadline_warning", "risk.created_high"], "description": "lg231 Agile integration" } ``` Risposta: `{ "secret": "whsec_...", "id": 1 }` → salvare `secret` in `companies.metadata.nis2_webhook_secret` --- ## Compatibilità tenant NIS2 usa `organization_id` (singolo DB). lg231 usa `company_id` + `tenant_id` (multi-tenant). La chiave API NIS2 è **già legata a una specifica organizzazione**: non serve passare `org_id` negli header. Salvare `nis2_org_id` in lg231 è opzionale (solo per riferimento UI). --- ## Checklist implementazione lg231 - [ ] `company-ms`: aggiungere `nis2_api_key`, `nis2_org_id`, `nis2_enabled` a `provider-config` - [ ] `shared/nis2-lib/src/Nis2Client.php`: client HTTP leggero - [ ] Widget NIS2 nella company detail view (HTML + JS) - [ ] `risk-ms`: import rischi cyber durante assessment - [ ] `monitoring-ms`: escalation incidenti significativi → OdV activity - [ ] (opzionale) endpoint webhook ricevente + subscription NIS2 - [ ] Test: `GET /api/services/status` deve rispondere 200 senza auth --- ## Note tecniche NIS2 - **Modello**: PHP 8.4, Front Controller, no framework - **Auth**: API Key sha256-hashed in DB (`api_keys` table) - **Scopes**: `read:all`, `read:compliance`, `read:risks`, `read:incidents`, `read:assets`, `read:policies` - **Tabella**: `api_keys (id, organization_id, name, key_prefix, key_hash, scopes JSON, is_active, expires_at, last_used_at)` - **Rate limit**: 100 req/h per chiave, file-based in `/tmp/nis2_ratelimit/` - **CORS**: origine specifica (no wildcard) — lg231 deve fare chiamate server-to-server, NON da browser --- *Generato: 2026-03-07 | NIS2 Agile v1.0 | Contatto: cristiano.benassati@gmail.com*