
O Engagement Manager é uma aplicação web para rastrear engajamentos de segurança ofensiva. Ele apresenta uma interface moderna, construída com Next.js, Prisma e PostgreSQL.
Engagement Manager é uma aplicação web para rastrear engajamentos de segurança ofensiva. Possui uma UI moderna, construída com Next.js, Prisma e PostgreSQL. A aplicação inclui um calendário, engajamentos, clientes, contatos, achados e operadores.

| Família de scanner/exportação | Exportação aceita |
|---|---|
| Burp Suite | Issues XML, incluindo o DTD de esquema interno inerte |
| Nessus / Tenable | Nessus v2 XML (.nessus) |
| Nmap | XML; portas abertas e sua saída de script tornam-se observações informativas, não vulnerabilidades inferidas |
| OpenVAS / Greenbone | Relatório XML nativo ou GMP get_reports_response |
| OWASP ZAP | Relatório JSON tradicional com sites e alertas |
| Nuclei | JSON Lines (-jsonl) |
| Qualys | XML de resultado de varredura (estrutura SCAN/IP), não o formato separado da API de detecção de hosts |
| Semgrep / CodeQL e outros produtores SARIF | Execuções, regras e resultados SARIF JSON |
As exportações são limitadas a 2 MB e 500 achados por importação, com limites de taxa de pré-visualização e confirmação por usuário. A aplicação aceita no máximo 10.000 achados no total e 500 para um engajamento entre criação manual, modelos e importações de scanner. A lista global de Findings carrega 100 linhas por página, e consultas de achados de engajamento/relatório são limitadas pelo mesmo limite por engajamento. Layouts desconhecidos falham visivelmente em vez de serem silenciosamente tratados como uma importação bem-sucedida. As severidades do scanner são sugestões: revise seu contexto antes da aprovação. URLs referenciadas, HTML e imagens remotas incorporadas não são buscadas ou executadas.
Relatórios permitem 1–100 achados, até 100 imagens de evidência (5 MB cada, 20 MB de entrada total), 500 páginas e 25 MB de saída. A emissão é limitada a 50 versões por engajamento e 1 GB de PDFs emitidos em toda a aplicação. Pré-visualização e emissão têm limites de taxa por usuário, e apenas uma renderização de PDF é admitida por processo da aplicação por vez. Achados retêm no máximo 1000 revisões e 500 comentários; atingir um limite falha sem sobrescrever o histórico. Fontes DejaVu e sua licença de redistribuição estão incluídas em assets/fonts; as implantações devem reter esses assets (o rastreamento de saída do Next os inclui).
As Server Actions do Next.js compartilham um único limite de tamanho de corpo de 25mb (definido em next.config.ts) para uploads de evidências. O login usa uma rota dedicada de mesma origem codificada em URL com um limite de streaming de 4 KB antes da autenticação ou trabalho no banco de dados.
Isso preserva o espaço de trabalho autenticado compartilhado existente, não um novo modelo de tenancy por cliente. Todas as novas páginas, ações e downloads de PDF verificam uma sessão atual respaldada pelo banco de dados. Rascunhos são limitados ao seu proprietário; permissões de revisão, aprovação de modelo e emissão são aplicadas no lado do servidor. Respostas de PDF confidenciais são private/no-store. PDFs finais contêm apenas uma allowlist explícita de campos de relatório, nunca rascunhos privados, comentários de revisão ou engajamentos não relacionados.
A implementação usa a checklist OWASP Top 10:2025: verificações de acesso (A01), respostas privadas e controles CSP/CSRF existentes (A02), dependências fixadas e CI (A03), proteções existentes de sessão/segredo mais verificações de integridade de relatório (A04/A08), Markdown/XML inertes e acesso parametrizado ao banco de dados (A05), processamento limitado e revisão independente (A06), verificações de sessão ao vivo (A07), eventos de auditoria sem conteúdo (A09) e alterações transacionais com limpeza em caso de falha (A10). Um digest detecta corrupção acidental; não é uma assinatura digital ou proteção contra um administrador de banco de dados. Isso não é uma certificação de conformidade. A produção ainda requer HTTPS, armazenamento protegido de banco de dados/backup e monitoramento operacional da saída de auditoria.
Antes de implantar esta atualização, faça um backup normal da aplicação e aplique as migrações aditivas 20260904221808_reporting_workflow e 20260906194500_add_revocable_sessions com npm run db:migrate, depois regenere o Prisma Client e reconstrua. Achados existentes começam como Draft na versão 1, e cookies de navegador existentes devem fazer login novamente para receber um ID de sessão respaldado pelo servidor. Não redefina um banco de dados existente. Backups incluem as novas tabelas e PDFs emitidos através da exportação completa existente do banco de dados.
npm test npm run lint npx tsc --noEmit --noUnusedLocals --noUnusedParameters npm run build npm audit
`npm test` usa o modo de teste não isolado do Node com `tsx` para que os casos de teste TypeScript individuais sejam executados, em vez de apenas relatar o sucesso do subprocesso do arquivo. Mantenha os totais de asserções explícitos visíveis no CI.
As regressões de banco de dados e navegador exigem um **banco de dados local dedicado chamado `reporting_tests`**, com as migrações aplicadas. Elas criam e excluem suas próprias linhas de fixture; nunca aponte esses testes para um banco de dados de aplicação. Defina `REPORTING_TEST_DATABASE_URL` para esse banco de dados de teste e, em seguida, execute:```bash
DATABASE_URL="$REPORTING_TEST_DATABASE_URL" npx prisma migrate deploy
npm run test:reporting
npx playwright install chromium
npm run test:browser
O conjunto de testes do navegador inicia seu próprio servidor de desenvolvimento em loopback na porta 3317 com um segredo de sessão exclusivo para testes; ele se recusa a reutilizar um servidor existente. Defina REPORTING_TEST_BROWSER para um executável Chromium instalado, se desejado. Ele testa privacidade de rascunho, edições conflitantes, upload de evidências, revisão independente, permissões/imutabilidade de PDF, criação de modelo sem JavaScript e importações seletivas desduplicadas. Os testes de integração exercitam conflitos transacionais reais e rollback. Os conjuntos de testes não substituem a verificação de LAN remota, Safari ou implantação em produção.
Este aplicativo foi projetado para ser executado no Ubuntu e requer o seguinte:```bash sudo apt update && sudo apt install -y nodejs npm postgresql postgresql-client postgresql-contrib zip
`postgresql-client` fornece `pg_dump`, `pg_restore` e `psql`; `zip` cria arquivos de backup. A extração de restauração é tratada pela aplicação com validação estrita de entradas e tamanho.
A instalação dos pacotes nem sempre deixa o PostgreSQL em execução. Inicie e habilite o serviço antes de criar funções ou iniciar a aplicação:```bash
sudo systemctl enable --now postgresql
sudo systemctl status postgresql --no-pager
Se o aplicativo falhar mais tarde com Can't reach database server at 127.0.0.1:5432, execute sudo systemctl start postgresql e confirme com pg_isready -h 127.0.0.1 -p 5432.
O aplicativo requer Node.js ^22.12.0 ou >=24.0.0 (consulte engines em package.json). Se o pacote do sistema operacional for mais antigo, instale uma versão suportada de uma fonte de pacotes confiável cujas assinaturas você verifique antes de executar setup.sh.
Crie um arquivo .env na raiz do projeto antes de executar o Prisma ou o aplicativo:```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
| Variável | Obrigatória | Notas |
|----------|----------|-------|
| `DATABASE_URL` | Sim | String de conexão PostgreSQL. O Prisma usa o parâmetro de consulta `schema=public`. O backup e a restauração usam um arquivo pgpass temporário somente para o proprietário, para que a senha não seja colocada em argumentos de subprocesso. |
| `JWT_SECRET` | Sim em produção | Deve ter pelo menos **32 caracteres**. A aplicação recusa-se a iniciar em produção sem ele. A rotação deste valor invalida todas as sessões existentes. |
| `TRUST_PROXY` | Não | Defina como `1` (ou `true`) apenas quando a aplicação estiver atrás de um proxy reverso que **sobrescreve** `X-Forwarded-For` / `X-Real-IP` e `X-Forwarded-Host`. As verificações de origem de login usam `X-Forwarded-Host` quando presente neste modo; deve conter um host público, incluindo uma porta não padrão quando utilizada. Caso contrário, o proxy deve preservar o cabeçalho `Host` público. Esta é a topologia de produção necessária para limites precisos de login por origem. Quando não definido, os cabeçalhos são ignorados para evitar falsificação e o login usa um orçamento de fallback compartilhado de um minuto mais elevado, para que um cliente não possa impor um bloqueio global de 15 minutos. |
| `ALLOWED_DEV_ORIGINS` | Não | **Apenas em desenvolvimento.** Nomes de host adicionais autorizados a carregar recursos `/_next` (separados por vírgulas). Os endereços IPv4 atuais da LAN do servidor são permitidos automaticamente. Use isto para um nome DNS estável. As compilações de produção ignoram isto. |
Gere um segredo forte:```bash
openssl rand -base64 32
Certifique-se de que o PostgreSQL está em execução primeiro (consulte Pré-requisitos). O ./setup.sh automatizado inicia o serviço por você; as etapas manuais abaixo assumem que ele já está ativo.
Execute os seguintes comandos para criar o banco de dados PostgreSQL e o usuário:```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;"
### Produção
Use um usuário de banco de dados dedicado com **privilégio mínimo** — não conceda `CREATEDB` nem direitos de superusuário:```bash
sudo -u postgres createuser --pwprompt em_app
sudo -u postgres createdb --owner=em_app engagement_manager
Defina DATABASE_URL para usar em_app (ou o nome de usuário escolhido). As migrações são executadas como este usuário através de npm run db:migrate.
Nota: Os arquivos do banco de dados são armazenados no diretório de dados do PostgreSQL (tipicamente
/var/lib/postgresql/<version>/main/).
A partir da raiz do repositório, execute:```bash chmod +x setup.sh ./setup.sh
O script instala os pré-requisitos, inicia e habilita o serviço PostgreSQL, solicita um nome de usuário e senha do banco de dados, grava um `.env` com `chmod 600`, cria a role e o banco de dados PostgreSQL, aplica as migrações e popula a conta de administrador padrão. O modo de produção também conclui o `npm run build` e imprime apenas o comando de inicialização de produção. Ele não instala o Node.js a partir de um script de shell remoto; instale primeiro uma versão suportada do Node.js.
Para uso headless ou em 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
Execute ./setup.sh --help para ver todas as opções.
--db-pass=... foi removido porque segredos na linha de comandos são visíveis para outros processos. Coloque a palavra-passe num ficheiro acessível apenas pelo proprietário e substitua o argumento antigo por --db-pass-file=/secure/db-password; o exemplo de configuração automatizada acima está pronto a copiar e colar.setup.sh já não instala o Node.js. Instale uma versão suportada do Node.js (^22.12.0 ou >=24.0.0) a partir de uma fonte de pacotes fidedigna antes de o executar.npm ci, pelo que o package-lock.json tem de estar presente e sincronizado com o package.json..sql legados não podem ser restaurados. Antes de desativar um servidor antigo, atualize-o para uma versão que consiga criar o backup estruturado da aplicação e reexporte os dados como um .zip.A partir do diretório do projeto, um único comando instala as atualizações de pacotes, inicia o PostgreSQL caso esteja parado e inicia a aplicação:```bash ./run.sh
Deixe essa janela aberta. Use o endereço Local ou Network que ela exibe.
Para iniciá-lo você mesmo: o PostgreSQL precisa estar em execução (`sudo systemctl start postgresql` se necessário). Em seguida, inicie o servidor de desenvolvimento:```bash
npm run dev
Startup imprime tanto uma URL de loopback quanto o endereço LAN desta máquina:```
`npm run dev` e `npm start` vinculam a `0.0.0.0` para que a URL de Rede funcione na LAN. Trate o acesso à LAN como exclusivo para laboratório em uma rede confiável. O modo de desenvolvimento não está reforçado para a internet pública.
Se você abrir o aplicativo por **hostname** (não IP) e o navegador remoto exibir uma página branca em branco, adicione esse nome ao `.env` e reinicie:```bash
ALLOWED_DEV_ORIGINS=dev.office.example
^22.12.0 ou >=24.0.0 (consulte engines em package.json)Secure em produção.uploads/ (capturas de tela de achados)Clone o repositório e instale as dependências: ```bash npm ci
Crie .env com valores de produção (DATABASE_URL, JWT_SECRET ≥ 32 caracteres).
Aplique as migrações do banco de dados: ```bash npm run db:migrate
Execute as verificações pré-implantação: ```bash npm run audit npm run typecheck npm run build
Inicie a aplicação com NODE_ENV=production: ```bash
NODE_ENV=production npm run start
Para um servidor real, execute isto sob um gestor de processos (systemd, PM2, etc.) e coloque um proxy reverso à frente para terminação TLS.
JWT_SECRET tem pelo menos 32 caracteres e não está comprometido no gitNODE_ENV=production está definido para o processo em execuçãoCREATEDB nem de superutilizadoruploads/ está em disco persistente e incluído nas cópias de segurançabackups/ está em disco persistente se os administradores usarem Backuppg_dump, pg_restore e zip estão disponíveis se os administradores forem usar Backup/RestoreApós o seed da base de dados, pode iniciar sessão utilizando a conta de administrador temporária gerada:
admininitial-admin-credentials.txt com acesso apenas ao proprietário por npx prisma db seed / npm run db:seedNota: Ser-lhe-á exigido que altere esta palavra-passe temporária no primeiro início de sessão. Elimine
initial-admin-credentials.txtimediatamente depois. Todas as palavras-passe devem ter pelo menos 16 caracteres e incluir uma letra maiúscula, uma letra minúscula, um número e um símbolo.
/dashboard/users).Em Admin, o painel Database mostra os botões Backup, Restore e Reset. O painel Users lista as contas e fornece um botão New User para adicionar utilizadores. O painel Appearance permite ao administrador escolher a cor de destaque de toda a aplicação.
Backup requer a sua palavra-passe de administrador e, em seguida, guarda um .zip com o nome em-backup-YYYY-MM-DD-HHMM.zip em backups/ no diretório da aplicação (engagement-mgr/backups/). Após uma exportação bem-sucedida, utilize Download na página Admin. Uma autorização assinada de curta duração é mantida num cookie HttpOnly e só funciona para o administrador que criou a cópia de segurança.
em-backup-2026-06-02-1430.zip.| Caminho | Conteúdo |
|---|---|
engagement-manager-backup/database.dump | Despejo completo do PostgreSQL em formato personalizado (esquema, tabelas, dados, enums, relações) do pg_dump |
engagement-manager-backup/uploads/ | Ficheiros de captura de ecrã de achados referenciados na base de dados |
.zip criado por Backup e substitui a base de dados atual e a pasta uploads/. A restauração pelo navegador está limitada a 8 MB para que a descompressão não monopolize o processo web. Para um arquivo maior, pare a aplicação e execute npm run db:restore -- /absolute/path/to/em-backup.zip como o utilizador da aplicação. O comando offline carrega o .env a partir do diretório de trabalho e requer um DATABASE_URL não vazio no .env ou no ambiente. Aceita ficheiros regulares até 500 MB e transmite cada entrada do arquivo através do seu limite de tamanho expandido. A restauração da base de dados é executada numa única transação; as contagens de entradas do arquivo, os caminhos, as taxas de compressão e os tamanhos expandidos são validados antes de os ficheiros serem instalados. Backup, restore, reset e alterações de ficheiros de captura de ecrã partilham um bloqueio de manutenção exclusivo para que as confirmações da base de dados e as trocas do sistema de ficheiros não possam sobrepor-se. Requer a sua palavra-passe de administrador para confirmar.admin. Requer escrever RESET e reintroduzir a palavra-passe atual do administrador que confirma. Essa palavra-passe torna-se a palavra-passe temporária da conta recriada e deve ser alterada no primeiro início de sessão.Servidor antigo
.zip e copie-o para o novo servidor (por exemplo com scp ou rsync): ```bash
scp em-backup-2026-06-02-1430.zip user@new-server:/path/to/
Novo servidor
.env com DATABASE_URL e JWT_SECRET (consulte Configuração de Ambiente).npm ci.admin usando o initial-admin-credentials.txt exclusivo do proprietário, altere a palavra-passe temporária e elimine o ficheiro de credenciais./dashboard/users), clique em Restore (em Database), selecione o .zip do servidor antigo, introduza a sua palavra-passe de administrador e confirme.Notas
uploads/.git clone (ou implemente a mesma revisão) no novo servidor para que a aplicação corresponda ao esquema esperado pelo backup. Se o servidor antigo executava um esquema mais recente do que o código clonado, alinhe as versões antes de importar.Esta secção documenta a arquitetura, o esquema da base de dados, as medidas de segurança e as fases de desenvolvimento concluídas para a aplicação Engagement Manager.
Modal.tsx e .modal-panel em globals.css.Adições de reporting: Finding também armazena version, reviewStatus, authorId, reviewerId, templateId e importFingerprint; Screenshot armazena sortOrder. FindingTemplate contém texto reutilizável revisto; FindingRevision contém revisões de texto imutáveis; FindingDraft contém rascunhos privados por utilizador com versões de conflito; FindingComment regista discussões de revisão; EngagementReport contém o título do relatório, o resumo executivo e os IDs de findings ordenados; IssuedReport armazena um PDF imutável, um snapshot de conteúdo e um digest SHA-256 para cada versão emitida. As relações de autor/revisor de utilizador usam SetNull; os rascunhos privados são removidos quando o seu utilizador é removido. Os registos de reporting seguem o ciclo de vida do engagement/finding pai.
id, username, passwordHash, role (Admin, User), lastPasswordChange, lastLogin, sessions, createdAt, updatedAt.id, userId, expiresAt, createdAt — os registos do lado do servidor tornam cada sessão de login assinada individualmente revogável no logout.key, count, resetAt — reservas atómicas de tentativas de origem e de confirmação de palavra-passe. A verificação de palavra-passe também tem um limite de concorrência delimitado.highlightColor (Red, Blue, Teal, Green, Purple ou 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 com Contact), , , , .id, company (coluna da BD: companyName), address, city, state, zip, phone (coluna da BD: phoneNumber), website, notes, contacts, engagements, createdAt, updatedAt.id, clientId, name, title, email, phone (coluna da BD: phoneNumber), notes, assignedEngagements, trustedEngagements, createdAt, updatedAt.id, engagementId (opcional), title, category, severity, background, remediation, supportingData (coluna da BD: 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.Para adicionar um novo campo a um modelo existente (por exemplo, focus em Engagement):
prisma/schema.prisma e adicione o campo ao modelo pretendido: ```prisma
model Engagement {
id String @id @default(uuid())
codeName String
focus String? // new field
...
}
prisma/schema.prisma deve ser seguida de: ```bash
npx prisma migrate dev --name describe_your_change
Isso cria uma migração, atualiza o banco de dados e regenera os tipos do Prisma Client.
admin padrão é gerada via seed do Prisma. Os papéis Admin têm acesso total de criação/edição/exclusão a todos os registros. Os papéis User podem criar, editar e excluir findings e screenshots; todas as outras entidades (engagements, clients, contacts, operators) são somente leitura para usuários. Cada página do dashboard atualiza a sessão em relação ao banco de dados antes de ler dados confidenciais. Apenas admins podem acessar a página Admin (/dashboard/users), gerenciar contas, alterar a cor de destaque de toda a aplicação e fazer backup, restaurar ou redefinir o banco de dados. Backup, restauração e redefinição exigem reconfirmação de senha. A criação de um backup é uma Server Action; o download pelo navegador usa GET /api/db/backup?file=… com a sessão de Admin e uma concessão assinada de cinco minutos em um cookie HttpOnly.jose armazenados em cookies HttpOnly, SameSite=Lax e uma linha Session correspondente no lado do servidor que o logout revoga. A expiração do cookie é intencionalmente omitida para manter o comportamento de sessão do navegador; tanto o token assinado quanto o registro no banco de dados expiram após um dia. A admissão transacional retém no máximo dez sessões ativas por conta.src/proxy.ts) impõe verificações de sessão e rotação de senha a cada 90 dias em todas as rotas protegidas./api/uploads previne IDOR e retorna respostas no-store.endReportingoutbriefobjectivestargetsexclusionsnotesoperatorscontactstrustedAgentsfindingsfindingContextscreatedAtupdatedAt