Due vulnerabilità trovate dalla review indipendente:
1. connectorOrgGuard usava users.role (GLOBALE) invece del ruolo per-org -> la feature
era ROTTA per gli utenti reali (org_admin reale ha users.role='employee' -> 403 sulla
propria org). Ora ancora l'autorizzazione al parametro di ROUTE {id} e legge
user_organizations.role. Verificato E2E: globale=employee + per-org=org_admin -> 200;
non-membro su altra org -> 403 (no IDOR via header X-Organization-Id).
2. secret-strip era una denylist case-sensitive/non-ricorsiva aggirabile (Client_Secret,
apiKey, connection_string, segreti annidati). Sostituita con ALLOWLIST ricorsiva
(sanitizeConnectorConfig): solo campi non sensibili noti, valori forzati a stringa+troncati.
Verificato E2E: input con 11 varianti di segreti -> DB contiene solo {account_id, region}.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Verificato E2E in prod: list 200 (8 tipi), save m365 201, secret 'client_secret' STRIPPATO (assente da config DB), delete 200, openConnectors servito in companies.html.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
I commit 56ce97d/1a5db30/14c06c8 contenevano migrazioni+HTML ma gli Edit dei
metodi controller e delle route erano falliti silenziosamente (ancore errate).
Ora presenti e testati E2E in produzione:
- DashboardController::sectorBenchmark (era 501)
- SupplyChainController: sendQuestionnaire/publicQuestionnaire/submitPublicQuestionnaire/questionnaireStatus/resolveQuestionnaire + route 'supply-chain' (era 404)
- PolicyController: attest/attestations/versions/diff/pendingAttestations + snapshot in approve + route (era 404)
Test: benchmark 200, supplier flow send->submit(score 61)->dedup 409->DB risk_score=39,
policy approve->attest(coverage 50%)->bump v2.0->diff(+2/-1)->pending ricompare.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Il commit 372ccb5 aveva incluso versioni con Edit falliti (ancore errate):
- AuditController::controlsMonitoring ora effettivamente presente (era 501 in prod)
- ServicesController::openapi ora espone incidents-ingest/evidence-ingest/assets-ingest/controls-monitoring
- i18n.js: chiavi nel formato corretto {it,en} (risks.fair_tab/kri_tab, assets.import_btn, audit.monitoring_tab)
- help.js: sezione Monitoraggio Continuo in reports
Verificato in prod: openapi 4/4, controlsMonitoring/fairRegister/kri tutti 200.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Il commit 1be3bd0 conteneva migrazione 026 + FairService + route ma NON i metodi
computeFair/fairRegister/listKri/createKri/updateKri nel controller (Edit fallito
per ancora errata). Ora presenti e testati E2E in prod:
- FAIR compute ALE Monte Carlo (risk 432: ALE mean 174.806 EUR, deterministico)
- fairRegister portfolio ALE, KRI create/update/dashboard con semaforo amber->red
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Il FAB ARIA (common.js) chiamava POST /api/ai/ask ma il controller non esisteva
(assistente AI rotto). Creato AiController::ask -> AIService::askWithRag con RAG su KB
+ grounding fonti certe. Verificato in produzione: rag_used=True, cita Ambiti NIS2 / Determina ACN.
Fix DNS Qdrant: nei worker php-fpm (musl) getenv e gethostbyname NON funzionano per
hostname Docker single-label; funziona solo un IP letterale. VectorService fallback ->
172.21.0.3 (fpm-safe); QDRANT_URL compose resta hostname per CLI. Vedi nota drift in VectorService.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- ServicesController: nuovo endpoint GET /api/services/full-snapshot
Aggrega gap-analysis, measures, incidents, training, deadlines,
compliance-summary in una sola chiamata (reduce 6 round-trip → 1)
Parametro ?days=N per finestra deadlines (default 30, max 365)
- public/index.php: route GET:fullSnapshot aggiunta all'action map services
- public/simulate-nis2-big.php: wrapper SSE per simulate-nis2-big.php
Esegue il simulatore come sottoprocesso CLI con NIS2_SSE=1 e
streama l'output al browser tramite Server-Sent Events
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GET /services/gap-analysis — gap per dominio NIS2 Art.21 con mapping MOG 231 pillars
- GET /services/measures — compliance_controls con mog_area e nis2_article derivati
- GET /services/incidents — incidenti con Art.23 CSIRT compliance per step (24h/72h/30d)
- GET /services/training — corsi + completamento board (Art.20 compliance flag)
- GET /services/deadlines — scadenze aggregate da 4 sorgenti con ?days= filter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BaseController non ha costruttore — la chiamata parent::__construct()
causava Fatal Error "Cannot call constructor" su ogni richiesta.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- index.html: CTA "Registrati" → "Richiedi accesso" (anchor form)
Badge hero "Accesso su invito — Richiedi il tuo codice per iniziare"
Sezione #richiedi-accesso con form lead (nome, email, azienda, ruolo,
dimensioni, messaggio) + JS submit asincrono + stato successo/errore
CTA finale aggiornato con messaggio codice invito
- ContactController.php: POST /api/contact/request-invite
Validazione campi, rate limit 3/10min per IP, email a info@agile.software
tramite EmailService con template HTML branded
- index.php: route contact → ContactController + action requestInvite
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ServicesController::provision(): JWT usa user_id (standard requireAuth)
- simulate-nis2.php: classifyOrg null-safe per entity_type
- simulate-nis2.php: completeOnboarding usa PUT /organizations/{id}
invece di /onboarding/complete (evita 409 quando org già esiste)
- simulate-nis2.php: supplier.critical rimosso da $supDef (was extra field)
- EmailService: rimosso sent_at (non in email_log schema)
- WebhookService: status ?? 'detected' (null-safe)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ServicesController::provision(): INSERT users usa full_name/is_active (non first_name/last_name/status)
- ServicesController::ssoLogin(): stesso fix per SSO user creation
- simulate-nis2::ensureUser(): registration payload usa full_name (non first_name+last_name)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AuthController:
- register() accetta `role` diretto (compliance_manager, org_admin, auditor, board_member, consultant)
- Aggiunto validateInvite() → POST /api/auth/validate-invite (no auth)
OnboardingController:
- Aggiunto lookupPiva() → POST /api/onboarding/lookup-piva (no auth, rate limit 10/min)
usato da register.html per P.IVA lookup pre-login
Router (index.php):
- Aggiunto POST:validateInvite e POST:lookupPiva
api.js:
- register() invia sia `role` che `user_type` per retrocompatibilità
simulate-nis2.php:
- SIM-06: B2B provisioning via X-Provision-Secret → org + JWT + API Key
- Filtro NIS2_SIM=SIM06 via goto per skip SIM-01→05 indipendenti
- readEnvValue() helper per leggere PROVISION_SECRET da .env
register.html:
- lookupPiva usa /onboarding/lookup-piva (endpoint pubblico)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Database non ha metodo execute() — corretto in:
InviteController, ServicesController, WebhookController,
NormativeController, WhistleblowingController.
Causa del HTTP 500 su tutti gli endpoint /api/invites/*.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ServicesController:
- POST /api/services/token: lg231 invia API key → riceve JWT 15min
- POST /api/services/sso: SSO federato con identità utente + responsabilità
→ crea/trova utente NIS2 + emette JWT 2h con ruolo e responsibilities
- Audit trail: ogni chiamata esterna autenticata loggata (api.external_call)
- SSO login loggato come auth.sso_login severity=warning con responsabilità
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Tutti i riferimenti nis2.certisource.it → nis2.agile.software
- Apache vhost HTTP nis2.agile.software attivo su Hetzner
- Script setup-nis2-agile-software.sh: certbot SSL + redirect da vecchio dominio
- .env server: APP_URL aggiornato a https://nis2.agile.software
- CLAUDE.md, docs commerciali, integrazioni, API docs aggiornati
DNS da aggiungere in Cloudflare: nis2.agile.software A 135.181.149.254 (proxy OFF)
Poi eseguire: bash /opt/devenv/scripts/setup-nis2-agile-software.sh
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>