
Engagement Manager è un'applicazione web per il monitoraggio delle attività di sicurezza offensiva. Presenta un'interfaccia utente moderna, realizzata con Next.js, Prisma e PostgreSQL.
Engagement Manager è un'applicazione web per il monitoraggio delle attività di sicurezza offensiva. Presenta un'interfaccia utente moderna, costruita con Next.js, Prisma e PostgreSQL. L'app include un calendario, attività, clienti, contatti, risultati e operatori.

| Famiglia scanner/export | Export accettato |
|---|---|
| Burp Suite | Issues XML, incluso il DTD dello schema interno inerte |
| Nessus / Tenable | Nessus v2 XML (.nessus) |
| Nmap | XML; le porte aperte e il loro output degli script diventano osservazioni informative, non vulnerabilità dedotte |
| OpenVAS / Greenbone | Report XML nativo o GMP get_reports_response |
| OWASP ZAP | Report JSON tradizionale con siti e avvisi |
| Nuclei | JSON Lines (-jsonl) |
| Qualys | XML dei risultati di scansione (struttura SCAN/IP), non il formato separato dell'API di rilevamento host |
| Semgrep / CodeQL e altri produttori SARIF | Esecuzioni, regole e risultati SARIF JSON |
Gli export sono limitati a 2 MB e 500 risultati per importazione, con limiti di frequenza per utente per anteprima e conferma. L'applicazione accetta al massimo 10.000 risultati in totale e 500 per un singolo engagement tra creazione manuale, template e importazioni da scanner. L'elenco globale Findings carica 100 righe per pagina, e le query dei risultati di engagement/report sono limitate dallo stesso limite per engagement. I layout sconosciuti falliscono visibilmente invece di essere trattati silenziosamente come un'importazione riuscita. Le gravità degli scanner sono suggerimenti: rivedi il loro contesto prima dell'approvazione. Gli URL referenziati, l'HTML e le immagini remote incorporate non vengono recuperati né eseguiti.
I report consentono 1–100 risultati, fino a 100 immagini di prove (5 MB ciascuna, 20 MB di input totale), 500 pagine e 25 MB di output. L'emissione è limitata a 50 versioni per engagement e 1 GB di PDF emessi in tutta l'applicazione. L'anteprima e l'emissione hanno limiti di frequenza per utente, e viene ammesso un solo rendering PDF per processo applicativo alla volta. I risultati conservano al massimo 1000 revisioni e 500 commenti; il raggiungimento di un limite fallisce senza sovrascrivere la cronologia. I font DejaVu e la loro licenza di ridistribuzione sono inclusi in assets/fonts; i deployment devono conservare queste risorse (il tracing dell'output di Next le include).
Le Server Actions di Next.js condividono un unico limite di dimensione del corpo di 25mb (impostato in next.config.ts) per i caricamenti delle prove. Il login utilizza una route dedicata same-origin con codifica URL e un limite di streaming di 4 KB prima dell'autenticazione o delle operazioni sul database.
Questo preserva l'esistente area di lavoro autenticata condivisa, non un nuovo modello di tenancy per cliente. Tutte le nuove pagine, azioni e download PDF verificano una sessione corrente supportata dal database. Le bozze sono limitate al loro proprietario; i permessi di revisione, approvazione dei template ed emissione sono applicati lato server. Le risposte PDF riservate sono private/no-store. I PDF finali contengono solo una allowlist esplicita di campi del report, mai bozze private, commenti di revisione o engagement non correlati.
L'implementazione utilizza la checklist OWASP Top 10:2025: controlli di accesso (A01), risposte private e controlli CSP/CSRF esistenti (A02), dipendenze bloccate e CI (A03), protezioni esistenti di sessione/segreti più controlli di integrità dei report (A04/A08), Markdown/XML inerti e accesso al database parametrizzato (A05), elaborazione limitata e revisione indipendente (A06), controlli di sessione in tempo reale (A07), eventi di audit privi di contenuto (A09) e modifiche transazionali con pulizia in caso di errore (A10). Un digest rileva la corruzione accidentale; non è una firma digitale né una protezione da un amministratore del database. Questa non è una certificazione di conformità. La produzione richiede comunque HTTPS, archiviazione protetta di database/backup e monitoraggio operativo dell'output di audit.
Prima di distribuire questo aggiornamento, esegui un normale backup dell'applicazione e applica le migrazioni additive 20260904221808_reporting_workflow e 20260906194500_add_revocable_sessions con npm run db:migrate, quindi rigenera Prisma Client e ricompila. I risultati esistenti iniziano come Draft alla versione 1, e i cookie del browser esistenti devono effettuare nuovamente l'accesso per ricevere un ID di sessione supportato dal server. Non reimpostare un database esistente. I backup includono le nuove tabelle e i PDF emessi attraverso l'export completo del database esistente.
npm test npm run lint npx tsc --noEmit --noUnusedLocals --noUnusedParameters npm run build npm audit
`npm test` utilizza la modalità di test non isolata di Node con `tsx` in modo che i singoli casi di test TypeScript vengano eseguiti, invece di limitarsi a segnalare il successo del sottoprocesso del file. Mantieni visibili i totali espliciti delle asserzioni in CI.
Le regressioni del database e del browser richiedono un **database locale dedicato denominato `reporting_tests`**, con le migrazioni applicate. Creano ed eliminano le proprie righe di fixture; non puntare mai questi test a un database applicativo. Imposta `REPORTING_TEST_DATABASE_URL` su quel database di test, quindi esegui:```bash
DATABASE_URL="$REPORTING_TEST_DATABASE_URL" npx prisma migrate deploy
npm run test:reporting
npx playwright install chromium
npm run test:browser
La suite browser avvia un proprio server di sviluppo loopback sulla porta 3317 con un segreto di sessione riservato ai test; rifiuta di riutilizzare un server esistente. Impostare REPORTING_TEST_BROWSER su un eseguibile Chromium installato, se desiderato. Verifica la privacy delle bozze, le modifiche in conflitto, il caricamento delle prove, la revisione indipendente, i permessi/l'immutabilità dei PDF, la creazione di modelli senza JavaScript e le importazioni selettive deduplicate. I test di integrazione esercitano conflitti transazionali reali e rollback. Le suite non sostituiscono la verifica su LAN remota, Safari o distribuzione in produzione.
Questa applicazione è progettata per essere eseguita su Ubuntu e richiede quanto segue:```bash sudo apt update && sudo apt install -y nodejs npm postgresql postgresql-client postgresql-contrib zip
`postgresql-client` fornisce `pg_dump`, `pg_restore` e `psql`; `zip` crea archivi di backup. L'estrazione del ripristino è gestita dall'applicazione con una rigorosa validazione delle voci e delle dimensioni.
L'installazione dei pacchetti non sempre lascia PostgreSQL in esecuzione. Avvia e abilita il servizio prima di creare i ruoli o avviare l'app:```bash
sudo systemctl enable --now postgresql
sudo systemctl status postgresql --no-pager
Se in seguito l'app fallisce con Can't reach database server at 127.0.0.1:5432, esegui sudo systemctl start postgresql e verifica con pg_isready -h 127.0.0.1 -p 5432.
L'app richiede Node.js ^22.12.0 o >=24.0.0 (vedi engines in package.json). Se il pacchetto del sistema operativo è più vecchio, installa una release supportata da una fonte di pacchetti attendibile di cui verifichi le firme prima di eseguire setup.sh.
Crea un file .env nella radice del progetto prima di eseguire Prisma o l'app:```bash
cat > .env << 'EOF'
DATABASE_URL="postgresql://em_admin:em_pass@localhost:5432/engagement_manager?schema=public"
JWT_SECRET="replace-with-a-long-random-secret-at-least-32-characters"
EOF
chmod 600 .env
| Variabile | Obbligatoria | Note |
|----------|----------|-------|
| `DATABASE_URL` | Sì | Stringa di connessione PostgreSQL. Prisma utilizza il parametro di query `schema=public`. Il backup e il ripristino utilizzano un file pgpass temporaneo accessibile solo al proprietario, in modo che la password non venga inserita negli argomenti del sottoprocesso. |
| `JWT_SECRET` | Sì in produzione | Deve contenere almeno **32 caratteri**. L'app rifiuta di avviarsi in produzione senza di esso. La rotazione di questo valore invalida tutte le sessioni esistenti. |
| `TRUST_PROXY` | No | Impostare a `1` (o `true`) solo quando l'app si trova dietro un reverse proxy che **sovrascrive** `X-Forwarded-For` / `X-Real-IP` e `X-Forwarded-Host`. I controlli dell'origine di login utilizzano `X-Forwarded-Host` quando presente in questa modalità; deve contenere un host pubblico, incluso un numero di porta non predefinito quando utilizzato. In caso contrario, il proxy deve preservare l'header `Host` pubblico. Questa è la topologia di produzione richiesta per limiti di login accurati per origine. Quando non impostato, gli header vengono ignorati per prevenire lo spoofing e il login utilizza un budget di fallback condiviso di un minuto più elevato, in modo che un client non possa imporre un blocco globale di 15 minuti. |
| `ALLOWED_DEV_ORIGINS` | No | **Solo sviluppo.** Nomi host aggiuntivi autorizzati a caricare le risorse `/_next` (separati da virgola). Gli indirizzi IPv4 LAN correnti del server sono autorizzati automaticamente. Utilizzare questo per un nome DNS stabile. Le build di produzione ignorano questa impostazione. |
Genera un secret robusto:```bash
openssl rand -base64 32
Assicurati che PostgreSQL sia in esecuzione prima (vedi Prerequisiti). Lo script automatico ./setup.sh avvia il servizio per te; i passaggi manuali seguenti presuppongono che sia già attivo.
Esegui i seguenti comandi per creare il database PostgreSQL e l'utente:```bash sudo -u postgres createuser --pwprompt em_admin sudo -u postgres psql -c "ALTER USER em_admin CREATEDB;" sudo -u postgres createdb --owner=em_admin engagement_manager sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE engagement_manager TO em_admin;"
### Produzione
Utilizza un utente database dedicato con **privilegi minimi** — non concedere `CREATEDB` o diritti di superutente:```bash
sudo -u postgres createuser --pwprompt em_app
sudo -u postgres createdb --owner=em_app engagement_manager
Imposta DATABASE_URL per utilizzare em_app (o il nome utente scelto). Le migrazioni vengono eseguite come questo utente tramite npm run db:migrate.
Nota: I file del database sono memorizzati nella directory dati di PostgreSQL (tipicamente
/var/lib/postgresql/<version>/main/).
Dalla radice del repository, esegui:```bash chmod +x setup.sh ./setup.sh
Lo script installa i prerequisiti, avvia e abilita il servizio PostgreSQL, richiede un nome utente e una password per il database, scrive un file `.env` con `chmod 600`, crea il ruolo e il database PostgreSQL, applica le migrazioni e inizializza l'account amministratore predefinito. La modalità produzione completa anche `npm run build` e stampa solo il comando di avvio della produzione. Non installa Node.js da uno script shell remoto; installa prima una release supportata di Node.js.
Per uso headless o CI:```bash
sudo install -d -m 700 -o "$USER" /secure
openssl rand -base64 24 > /secure/db-password
chmod 600 /secure/db-password
./setup.sh -y --db-user=em_admin --db-pass-file=/secure/db-password
Esegui ./setup.sh --help per tutte le opzioni.
--db-pass=... è stato rimosso perché i segreti sulla riga di comando sono visibili ad altri processi. Inserisci la password in un file accessibile solo al proprietario e sostituisci il vecchio argomento con --db-pass-file=/secure/db-password; l'esempio di configurazione automatizzata sopra è pronto per il copia-incolla.setup.sh non installa più Node.js. Installa una versione supportata di Node.js (^22.12.0 o >=24.0.0) da una fonte di pacchetti attendibile prima di eseguirlo.npm ci, quindi package-lock.json deve essere presente e sincronizzato con package.json..sql non possono essere ripristinati. Prima di dismettere un vecchio server, aggiornalo a una versione in grado di creare il backup strutturato dell'applicazione e riesporta i dati come .zip.Dalla directory del progetto, un singolo comando installa gli aggiornamenti dei pacchetti, avvia PostgreSQL se è fermo e avvia l'applicazione:```bash ./run.sh
Lascia quella finestra aperta. Usa l'indirizzo Local o Network che stampa.
Per avviarlo manualmente invece: PostgreSQL deve essere in esecuzione (`sudo systemctl start postgresql` se necessario). Poi avvia il server di sviluppo:```bash
npm run dev
All'avvio vengono stampati sia un URL di loopback sia l'indirizzo LAN di questa macchina:```
`npm run dev` e `npm start` si legano a `0.0.0.0` in modo che l'URL di rete funzioni sulla LAN. Considera l'accesso LAN come solo per laboratorio su una rete attendibile. La modalità dev non è rafforzata per l'internet pubblico.
Se apri l'app tramite **hostname** (non IP) e il browser remoto mostra una pagina bianca vuota, aggiungi quel nome a `.env` e riavvia:```bash
ALLOWED_DEV_ORIGINS=dev.office.example
^22.12.0 o >=24.0.0 (vedi engines in package.json)Secure in produzione.uploads/ (screenshot delle vulnerabilità)Clona il repository e installa le dipendenze: ```bash npm ci
Creare .env con valori di produzione (DATABASE_URL, JWT_SECRET ≥ 32 caratteri).
Applicare le migrazioni del database: ```bash npm run db:migrate
Esegui i controlli pre-deploy: ```bash npm run audit npm run typecheck npm run build
Avvia l'applicazione con NODE_ENV=production: ```bash
NODE_ENV=production npm run start
Per un server reale, eseguilo sotto un process manager (systemd, PM2, ecc.) e metti un reverse proxy davanti per la terminazione TLS.
JWT_SECRET è di almeno 32 caratteri e non è committato su gitNODE_ENV=production è impostato per il processo in esecuzioneCREATEDB o superuseruploads/ è su disco persistente e incluso nei backupbackups/ è su disco persistente se gli admin usano Backuppg_dump, pg_restore e zip sono disponibili se gli admin useranno Backup/RestoreDopo aver eseguito il seed del database, puoi accedere usando l'account admin temporaneo generato:
admininitial-admin-credentials.txt accessibile solo al proprietario da npx prisma db seed / npm run db:seedNota: Ti verrà richiesto di cambiare questa password temporanea al primo accesso. Elimina
initial-admin-credentials.txtsubito dopo. Tutte le password devono essere di almeno 16 caratteri e includere una lettera maiuscola, una lettera minuscola, un numero e un simbolo.
/dashboard/users).Nella pagina Admin, il pannello Database mostra i pulsanti Backup, Restore e Reset. Il pannello Users elenca gli account e fornisce un pulsante New User per aggiungere utenti. Il pannello Appearance consente a un admin di scegliere il colore di evidenziazione a livello di applicazione.
Backup richiede la tua password admin, poi salva un file .zip denominato em-backup-YYYY-MM-DD-HHMM.zip in backups/ nella directory dell'applicazione (engagement-mgr/backups/). Dopo un'esportazione riuscita, usa Download nella pagina Admin. Una concessione firmata di breve durata è conservata in un cookie HttpOnly e funziona solo per l'admin che ha creato il backup.
em-backup-2026-06-02-1430.zip.| Percorso | Contenuto |
|---|---|
engagement-manager-backup/database.dump | Dump completo PostgreSQL in formato custom (schema, tabelle, dati, enum, relazioni) da pg_dump |
engagement-manager-backup/uploads/ | File screenshot dei finding referenziati nel database |
.zip creato da Backup e sostituisce il database corrente e la cartella uploads/. Il ripristino dal browser è limitato a 8 MB così la decompressione non può monopolizzare il processo web. Per un archivio più grande, ferma l'applicazione ed esegui npm run db:restore -- /absolute/path/to/em-backup.zip come utente dell'applicazione. Il comando offline carica .env dalla directory di lavoro e richiede un DATABASE_URL non vuoto in .env o nell'ambiente. Accetta file regolari fino a 500 MB e trasmette ogni voce dell'archivio attraverso il suo limite di dimensione espansa. Il ripristino del database viene eseguito in una singola transazione; il numero di voci dell'archivio, i percorsi, i rapporti di compressione e le dimensioni espanse vengono validati prima che i file vengano installati. Backup, restore, reset e modifiche ai file screenshot condividono un lock di manutenzione esclusivo, così i commit del database e gli scambi sul filesystem non possono sovrapporsi. Richiede la tua password admin per confermare.admin. Richiede di digitare RESET e di reinserire la password corrente dell'amministratore che conferma. Quella password diventa la password temporanea dell'account ricreato e deve essere cambiata al primo accesso.Vecchio server
.zip e copialo sul nuovo server (ad esempio con scp o rsync): ```bash
scp em-backup-2026-06-02-1430.zip user@new-server:/path/to/
Nuovo server
.env con DATABASE_URL e JWT_SECRET (vedi Configurazione dell'ambiente).npm ci.admin utilizzando il file initial-admin-credentials.txt riservato al proprietario, cambia la password temporanea ed elimina il file delle credenziali./dashboard/users), fai clic su Restore (sotto Database), seleziona il file .zip dal vecchio server, inserisci la tua password di amministratore e conferma.Note
uploads/.git clone (o distribuisci la stessa revisione) sul nuovo server in modo che l'applicazione corrisponda allo schema previsto dal backup. Se il vecchio server eseguiva uno schema più recente rispetto al codice clonato, allinea le versioni prima di importare.Questa sezione documenta l'architettura, lo schema del database, le misure di sicurezza e le fasi di sviluppo completate per l'applicazione Engagement Manager.
Modal.tsx e .modal-panel in globals.css.Aggiunte per il reporting: Finding memorizza anche version, reviewStatus, authorId, reviewerId, templateId e importFingerprint; Screenshot memorizza sortOrder. FindingTemplate contiene la formulazione riutilizzabile revisionata; FindingRevision contiene revisioni testuali immutabili; FindingDraft contiene bozze private per utente con versioni di conflitto; FindingComment registra le discussioni di revisione; EngagementReport contiene il titolo del report, il riepilogo esecutivo e gli ID dei finding ordinati; IssuedReport memorizza un PDF immutabile, uno snapshot del contenuto e un digest SHA-256 per ogni versione emessa. Le relazioni utente autore/revisore usano SetNull; le bozze private vengono rimosse quando il loro utente viene rimosso. I record di reporting seguono il ciclo di vita del loro engagement/finding padre.
id, username, passwordHash, role (Admin, User), lastPasswordChange, lastLogin, sessions, createdAt, updatedAt.id, userId, expiresAt, createdAt — i record lato server rendono ogni sessione di accesso firmata revocabile individualmente al logout.key, count, resetAt — prenotazioni atomiche dei tentativi di origine e di conferma password. La verifica della password ha anche un limite di concorrenza definito.highlightColor (Red, Blue, Teal, Green, Purple o Amber) e updatedAt.id, codeName, clientId, chargeCode, status (Prep, Recon, Testing, Reporting, Complete), focus, type (AI, Code_Review, Firewall, Multi, Pentest, Phishing, Physical, Purple_Team, Red_Team, USB_Drop, Vishing, Web_App, Wireless), location (Internal, External), startPrep, endPrep, startRecon, endRecon, startTesting, endTesting, startReporting, , , , , , , (M:N), / (M:N con Contact), , , , .id, company (colonna DB: companyName), address, city, state, zip, phone (colonna DB: phoneNumber), website, notes, contacts, engagements, createdAt, updatedAt.id, clientId, name, title, email, phone (colonna DB: phoneNumber), notes, assignedEngagements, trustedEngagements, createdAt, updatedAt.id, engagementId (opzionale), title, category, severity, background, remediation, supportingData (colonna DB: supportingLinks), screenshots, engagementContext, createdAt, updatedAt.id, engagementId, findingId, observation, affectedHosts, createdAt, updatedAt.id, findingId, filePath, description, createdAt.id, name, title, email, phoneNumber, discord, github, notes, engagements (M:N), createdAt, updatedAt.Per aggiungere un nuovo campo a un modello esistente (ad esempio, focus su Engagement):
prisma/schema.prisma e aggiungi il campo al modello desiderato: ```prisma
model Engagement {
id String @id @default(uuid())
codeName String
focus String? // new field
...
}
prisma/schema.prisma deve essere seguita da: ```bash
npx prisma migrate dev --name describe_your_change
Questo crea una migrazione, aggiorna il database e rigenera i tipi del Prisma Client.
admin viene generato tramite il seed di Prisma. I ruoli Admin hanno accesso completo in creazione/modifica/eliminazione a tutti i record. I ruoli User possono creare, modificare ed eliminare findings e screenshot; tutte le altre entità (engagements, clients, contacts, operators) sono di sola lettura per gli utenti. Ogni pagina della dashboard aggiorna la sessione rispetto al database prima di leggere dati riservati. Solo gli admin possono accedere alla pagina Admin (/dashboard/users), gestire gli account, modificare il colore di evidenziazione a livello di applicazione ed eseguire backup, ripristino o reset del database. Backup, ripristino e reset richiedono una riconferma della password. La creazione di un backup è una Server Action; il download dal browser utilizza GET /api/db/backup?file=… con la sessione Admin e una concessione firmata di cinque minuti in un cookie HttpOnly.jose memorizzati in cookie HttpOnly, SameSite=Lax e una riga Session corrispondente lato server che il logout revoca. La scadenza del cookie è intenzionalmente omessa per mantenere il comportamento di sessione del browser; sia il token firmato che il record nel database scadono dopo un giorno. L'ammissione transazionale mantiene al massimo dieci sessioni attive per account.src/proxy.ts) applica i controlli di sessione e la rotazione della password ogni 90 giorni su tutte le route protette./api/uploads previene l'IDOR e restituisce risposte no-store.endReportingoutbriefobjectivestargetsexclusionsnotesoperatorscontactstrustedAgentsfindingsfindingContextscreatedAtupdatedAt