diff --git a/backend/src/middleware/__tests__/auditFindings.test.ts b/backend/src/middleware/__tests__/auditFindings.test.ts new file mode 100644 index 0000000..1c31bc4 --- /dev/null +++ b/backend/src/middleware/__tests__/auditFindings.test.ts @@ -0,0 +1,56 @@ +/** + * Regression coverage for findings recorded in docs/AUDITORIA_BASE_2GT.md. + * + * These tests prove the *current* upstream behavior with evidence, rather than + * asserting an opinion. They are not fixes — see the audit report for + * classification, impact and recommendation of each finding. + */ +import { signToken, verifyToken, AuthPayload } from '../auth'; +import { resolveSiteId } from '../../utils/siteScope'; + +// ── SEC-001: JWTs are not invalidated by a password/role change ───────────── +// +// signToken/verifyToken never consult the database — a JWT is self-contained +// and valid for the full 24h `expiresIn` window regardless of what happens to +// the account afterward. There is no token version, jti, or revocation list +// consulted anywhere in requireAuth (see middleware/auth.ts). This test proves +// the mechanism: a token minted for a role has no way to be invalidated short +// of rotating JWT_SECRET (which invalidates every session at once). +describe('SEC-001: JWT sessions carry no revocation mechanism', () => { + it('a token remains valid and keeps its original role after being issued, ' + + 'with nothing in verifyToken that could observe a later password/role change', () => { + const payload: AuthPayload = { userId: 42, username: 'ops', role: 'admin' }; + const token = signToken(payload); + + // Simulate time passing / the account changing in the DB — verifyToken + // takes only the token, so it cannot know about that change. + const decoded = verifyToken(token); + + expect(decoded.role).toBe('admin'); + expect(decoded.userId).toBe(42); + // No exp check failure, no DB round-trip: the token is still authoritative + // for up to 24h from signing, whatever happened to the account since. + }); +}); + +// ── SEC-002: "sites" are a display filter, not an authorization boundary ──── +// +// resolveSiteId is the entire enforcement surface for site scoping: it turns +// a client-supplied header/query value into a SQL predicate. It has no +// knowledge of req.user and no lookup of which sites a user may access — +// confirmed by its signature (raw value in, id-or-null out) and by grep: no +// route file cross-references req.user with a site membership table (none +// exists in the schema). Any authenticated user (any role) can omit the +// header entirely to see every site's devices. +describe('SEC-002: site scoping has no per-user authorization component', () => { + it('resolves any syntactically valid site id with no user/session context ' + + 'available to the function at all', () => { + // Note the signature: no user, no session, no membership check — just a + // string. This is the one function that decides "which fleet subset", + // and it never sees who is asking. + expect(resolveSiteId('1')).toBe(1); + expect(resolveSiteId('2')).toBe(2); + expect(resolveSiteId(undefined)).toBeNull(); // unscoped = whole fleet + expect(resolveSiteId('all')).toBeNull(); // explicit whole fleet + }); +}); diff --git a/docs/AUDITORIA_BASE_2GT.md b/docs/AUDITORIA_BASE_2GT.md new file mode 100644 index 0000000..8f1ddd3 --- /dev/null +++ b/docs/AUDITORIA_BASE_2GT.md @@ -0,0 +1,222 @@ +# Auditoria Técnica e de Segurança — Base Upstream 2GT (MikroTik Manager) + +- Commit auditado: `1a4b6350528b8c656ab7a84ee4806a8a86cf9932` (v0.24.5-beta) +- Branch: `mikrormm-dev`, sobre bootstrap `7a4979e` +- Escopo: código-fonte completo (backend, frontend, Docker), sem RouterBOARD real conectada +- Método: leitura de código, grep dirigido, testes automatizados (jest/vitest), `npm audit`, análise de Dockerfiles/compose. Nenhuma credencial real foi usada ou impressa. + +## Mapa factual da arquitetura + +- **Frontend**: React 19 + Vite, SPA servida por nginx, autentica via Bearer JWT em `localStorage`. +- **Backend**: Node/Express (`backend/src/index.ts`), monta ~28 routers sob `/api/*`, todos com `router.use(requireAuth)` no topo do arquivo exceto os endpoints públicos explícitos de `oidc.ts` (`/status`, `/login`, `/callback`). +- **Autenticação**: `routes/auth.ts` + `middleware/auth.ts` — JWT HS256, bcryptjs, TOTP opcional (otpauth), OIDC opcional (openid-client). +- **Autorização**: 3 papéis globais (`admin`/`operator`/`viewer`) checados em `middleware/auth.ts` (`requireAdmin`, `requireWrite`). Sem escopo por site/tenant. +- **Banco**: PostgreSQL, schema em `db/migrate.ts` (migrations idempotentes, `CREATE TABLE IF NOT EXISTS` + `ALTER` incrementais), executado automaticamente no boot do backend. +- **Redis/BullMQ**: filas de polling/automação; rate limiting (`middleware/rateLimitRedis.ts`) também usa Redis com fallback em memória. +- **InfluxDB**: métricas de série temporal (`config/influxdb.ts`, consultado em `routes/metrics.ts`). +- **Jobs/Scheduler/Polling**: `services/PollerService.ts`, `services/DeviceBulkAddWorker.ts`. +- **Alertas**: `services/AlertService.ts` + `routes/alerts.ts`. +- **RouterOS integration**: `services/mikrotik/RouterOSClient.ts` (API binário RouterOS, TCP/TLS), `services/mikrotik/DeviceCollector.ts`. +- **SSH**: `services/sshExec.ts`, `services/SshKeyService.ts`, `services/BackupService.ts` (ssh2), terminal interativo via Socket.IO (`index.ts` namespace `/terminal`). +- **API**: ~28 arquivos de rota, ~250 endpoints. +- **SNMP**: não encontrada implementação SNMP no backend (ausente — não fazia parte do escopo upstream atual). +- **NetFlow**: `services/netflow/NetflowCollector.ts`, coletor UDP na porta 2055, exposto no `docker-compose.yml`. +- **Backups**: `services/BackupService.ts` + `routes/backups.ts`, arquivos `.rsc` (`/export compact`) em `backups_data` volume. +- **Firmware upgrade**: `services/FirmwareOrchestrator.ts` + `routes/firmware.ts`, waves com concorrência limitada (`utils/concurrency.ts`, `MAX_WAVE_CONCURRENCY=20`). +- **Terminal**: Socket.IO namespace `/terminal`, restrito a `admin`/`operator`, com auditoria e rate limit. +- **Change Guard**: `services/changeGuard/ChangeGuard.ts` — dead-man-switch no próprio dispositivo (scheduler RouterOS que desfaz a mudança se a conexão não for reconfirmada). +- **Auditoria**: `middleware/auditMiddleware.ts` (global) + tabela `audit_log`, consultável via `routes/auditLog.ts` (admin-only). + +## Achados + +### SEC-001 — Sessões JWT sem revogação server-side +**Severidade:** MÉDIO +**Componente:** `backend/src/middleware/auth.ts`, `routes/auth.ts` +**Evidência:** `signToken`/`verifyToken` não consultam o banco; `POST /api/auth/logout` só responde OK sem invalidar nada ("JWT is stateless; client just discards token"); `PUT /api/auth/password` e `POST /api/auth/totp/disable` não alteram nenhum estado que `verifyToken` observe. Comprovado por teste automatizado (`middleware/__tests__/auditFindings.test.ts`, SEC-001). +**Causa-raiz:** design stateless sem `jti`/versão de sessão nem lista de revogação. +**Impacto:** um token vazado (XSS, log, dispositivo comprometido) continua válido por até 24h mesmo depois de o usuário trocar a senha, desabilitar TOTP ou ser removido/rebaixado por um admin — só uma rotação de `JWT_SECRET` (que derruba TODAS as sessões) resolve. +**Exploração/condição:** requer vazamento prévio do token; não é explorável remotamente sem esse pré-requisito. +**Recomendação:** adicionar `jti` + tabela de revogação (ou coluna `token_version` em `users`, checada em `requireAuth`), e reduzir `expiresIn` para algo como 1–4h com refresh token. +**Status:** OPEN + +### SEC-002 — "Sites" são um filtro de exibição, não um limite de autorização +**Severidade:** ALTO (crítico para uso multi-cliente GTI; baixo para uso interno de um único tenant) +**Componente:** `backend/src/utils/siteScope.ts`, `middleware/site.ts`, schema `users` +**Evidência:** `siteContext` lê `X-Site-Id` do cliente sem checar identidade; `resolveSiteId` não recebe `req.user`; tabela `users` (`db/migrate.ts:6-12`) não tem nenhuma coluna/relação com `sites`. Comprovado por teste (`auditFindings.test.ts`, SEC-002) e por inspeção do schema. +**Causa-raiz:** o recurso "sites" (#130) foi implementado como agrupamento operacional (dashboards por site), não como fronteira de tenant. +**Impacto:** qualquer usuário autenticado — mesmo `viewer` — enxerga e (se `operator`/`admin`) opera sobre TODOS os dispositivos de TODOS os sites simplesmente omitindo o header `X-Site-Id`. Se a GTI pretende dar a um técnico/cliente acesso restrito a "seus" equipamentos, essa garantia **não existe hoje**. +**Recomendação:** antes de onboarding com múltiplos clientes reais no mesmo backend, implementar RBAC por site (tabela `user_sites`, checagem no middleware, nunca confiar em header sem cruzar com o usuário). +**Status:** OPEN + +### SEC-003 — RouterOS API-SSL sem verificação de certificado +**Severidade:** MÉDIO +**Componente:** `backend/src/services/mikrotik/RouterOSClient.ts:97` +**Evidência:** `tls.connect({ host, port, rejectUnauthorized: false }, ...)`. +**Causa-raiz:** RouterOS normalmente usa certificado autoassinado; verificar CA exigiria gerenciamento de certificados por dispositivo, não implementado. +**Impacto:** conexão à RouterBOARD é suscetível a MITM na rede entre backend e device — um atacante na mesma rede/segmento poderia interceptar credenciais RouterOS e comandos. +**Recomendação:** oferecer *trust-on-first-use* com fixação de fingerprint por dispositivo (guardar o hash do cert na primeira conexão e alertar/rejeitar se mudar), em vez de desabilitar globalmente. +**Status:** OPEN + +### SEC-004 — Chaves de criptografia default nunca expiram como fallback de decrypt +**Severidade:** MÉDIO +**Componente:** `backend/src/utils/secrets.ts:154-161`, `utils/crypto.ts` +**Evidência:** `encDecryptors` sempre inclui `KNOWN_DEFAULT_ENC` (as duas chaves públicas do repositório) na lista de chaves tentadas para decrypt, permanentemente, mesmo depois de anos de rotação. +**Causa-raiz:** decisão deliberada de nunca quebrar dados antigos cifrados com a chave default (documentada no próprio código). +**Impacto:** se uma instalação alguma vez rodou sem `ENCRYPTION_KEY` customizada (mesmo que temporariamente) e um `pg_dump` daquele período vazar, os segredos daquele período são decifráveis com uma chave pública e conhecida deste repositório — para sempre. +**Recomendação:** aceitável como *tradeoff* de migração, mas documentar explicitamente no runbook de produção; considerar expurgar as chaves default do array de decrypt após confirmar (via `needsReencryption`) que nenhum registro as usa mais. +**Status:** ACCEPTED (mitigação: `ENCRYPTION_KEY` DEV já é forte e aleatória desde a Missão 00; nunca esteve no default) + +### SEC-005 — Change Guard cobre apenas um subconjunto pequeno das operações de escrita +**Severidade:** MÉDIO (expectativa vs. realidade) +**Componente:** `backend/src/routes/devices.ts` (único chamador de `withSafeApply`), `routes/commands.ts`, terminal Socket.IO +**Evidência:** `grep -rl withSafeApply routes/` retorna **apenas** `devices.ts`, usado no fluxo de "preflight"/apply genérico de mudanças analisadas (`analyzeChange`). As dezenas de endpoints diretos de firewall, NAT, rotas, VLAN, bonds, endereços IP, interfaces (mesmo arquivo `devices.ts`) chamam a API RouterOS diretamente, sem snapshot nem dead-man-switch. `commands.ts` (execução em massa) e o terminal SSH interativo também não passam por `ChangeGuard`. +**Causa-raiz:** Change Guard foi construído para o fluxo guiado de "mudança arriscada analisada", não como interceptador universal de toda escrita. +**Impacto:** um operador pode remover uma rota, uma regra de firewall ou uma interface via endpoint direto (ou via terminal/comando em massa) e perder conectividade com o dispositivo sem qualquer rede de segurança automática. +**O que o Change Guard REALMENTE garante:** para o fluxo que passa por `withSafeApply`, garante rollback automático se a nova conexão de verificação falhar, com um restore point salvo no próprio dispositivo antes da mudança. +**O que ele NÃO garante:** proteção em firewall/NAT/rotas/VLAN/interfaces editados diretamente, em `commands.ts` (bulk), no terminal interativo, em `configHistory.ts` rollback, ou contra falha de hardware/queda de energia do device. +**Recomendação:** documentar essa cobertura parcial para os operadores GTI e avaliar estender `withSafeApply` aos endpoints de firewall/rotas/VLAN mais usados antes de operar em RBs de produção. +**Status:** OPEN (documentação atualizada nesta auditoria) + +### SEC-006 — Injeção de comando RouterOS via `ssh_username` não sanitizado +**Severidade:** BAIXO +**Componente:** `backend/src/services/SshKeyService.ts:196-204` +**Evidência:** `` `/user/ssh-keys/import public-key-file=${fileName} user=${username}` `` e `` `/file/remove ${fileName}` `` interpolam `username`/`fileName` sem escaping. `fileName` é gerado internamente (seguro); `username` vem de `target.ssh_username`, campo configurável por qualquer usuário `operator`/`admin` ao cadastrar o dispositivo. +**Causa-raiz:** ausência de validação/allowlist de caracteres para `ssh_username`. +**Impacto:** um operador que já tem permissão de configurar credenciais de um dispositivo (logo, já tem acesso legítimo a ele) poderia usar um `ssh_username` malformado para injetar comandos RouterOS adicionais na mesma sessão SSH. Não é escalação de privilégio (o ator já controla o dispositivo), mas quebra o princípio de defesa em profundidade. +**Recomendação:** validar `ssh_username` contra o alfabeto aceito pelo RouterOS para nomes de usuário antes de interpolar em qualquer comando. +**Status:** OPEN + +### SEC-007 — `nginx:alpine` sem tag de versão e demais imagens sem pinning por digest +**Severidade:** BAIXO (reprodutibilidade/supply chain) +**Componente:** `nginx/Dockerfile:12`, `docker-compose.yml` +**Evidência:** `FROM nginx:alpine` (sem versão nenhuma); `FROM node:26-alpine`; `postgres:15-alpine`, `redis:7-alpine`, `influxdb:2.7` — nenhuma imagem fixada por digest sha256. +**Impacto:** dois builds em datas diferentes podem produzir imagens de base diferentes (patches de SO, ou até uma nova major do nginx), reduzindo a reprodutibilidade entre DEV e PROD e dificultando auditoria de supply chain. +**Recomendação:** fixar ao menos a versão do nginx (`nginx:1.27-alpine`) e considerar pinning por digest em PROD. +**Status:** OPEN + +### SEC-008 — Dockerfiles usam `npm install` em vez de `npm ci` +**Severidade:** BAIXO +**Componente:** `backend/Dockerfile`, `nginx/Dockerfile` (estágio frontend-builder) +**Evidência:** ambos os estágios de build rodam `npm install` em vez de `npm ci`, apesar de existir `package-lock.json` no repositório. +**Impacto:** `npm install` pode resolver versões ligeiramente diferentes das travadas no lockfile em rebuilds futuros (dentro dos ranges semver), reduzindo reprodutibilidade bit-a-bit do build. +**Recomendação:** trocar para `npm ci` nos dois Dockerfiles. +**Status:** OPEN + +### SEC-009 — `qs`/`body-parser`/`express` — DoS moderado (transitivo) +**Severidade:** BAIXO +**Componente:** dependência transitiva `qs` (via `express@4.22.2` → `body-parser`) +**Evidência:** `npm audit --omit=dev` no backend reporta 3 vulnerabilidades moderadas (GHSA-x5fp-wj9c-mxmx, GHSA-4mjr-xmp4-gh2g) — bypass de limite de array / DoS via `isBuffer` manipulado. +**Contexto de exploração:** a API já tem rate limit global (120 req/min/IP, `rateLimitRedis`), reduzindo o valor prático do DoS. Correção disponível via `npm audit fix` dentro do range semver do Express 4.x. +**Recomendação:** aplicar o fix na próxima janela de manutenção de dependências (fora do escopo desta missão — "não atualizar dependências" foi respeitado aqui). +**Status:** OPEN (não corrigido nesta missão, por instrução explícita da missão) + +### SEC-010 — Rate limit de login é só por IP, sem lockout por conta +**Severidade:** BAIXO/INFORMATIVO +**Componente:** `backend/src/middleware/rateLimitRedis.ts` (`loginRateLimit`) +**Evidência:** chave `rl:login:ip:${ip}`; nenhuma chave por `username`. +**Impacto:** um atacante distribuído (múltiplos IPs/proxies) não é throttled por conta — mas a mensagem de erro é genérica ("Invalid credentials") em ambos os casos (usuário inexistente ou senha errada), então não há enumeração de usuários. +**Recomendação:** considerar um limite adicional por `username` além do IP. +**Status:** ACCEPTED (mitigação parcial já existe; risco baixo) + +### SEC-011 — Política de senha mínima aceitável, sem verificação de senha vazada +**Severidade:** INFORMATIVO +**Componente:** `backend/src/utils/passwordPolicy.ts` +**Evidência:** mínimo 10 caracteres, 1 letra, 1 número; sem checagem contra listas de senhas vazadas (ex. HaveIBeenPwned) nem bloqueio de senhas triviais como `Password123`. +**Recomendação:** aceitável para baseline; considerar reforço futuro. +**Status:** ACCEPTED + +### SEC-012 — Criptografia de credenciais RouterOS (AES-256-GCM) — bem desenhada +**Severidade:** INFORMATIVO (achado positivo) +**Componente:** `backend/src/utils/crypto.ts`, `utils/secrets.ts` +**Evidência:** AES-256-GCM com IV aleatório de 16 bytes por operação, tag de autenticação verificada (`setAuthTag`/`getAuthTag`), chave derivada via SHA-256 quando não tem exatamente 32 bytes, chave nunca persistida no banco (arquivo `secrets.json` fora do Postgres, modo `0o600`), suporte a rotação com múltiplas chaves de decrypt. Design correto e documentado no próprio código. +**Status:** FALSE_POSITIVE (não é um achado negativo — registrado como ponto forte confirmado) + +### SEC-013 — Backups: nomes de arquivo sanitizados corretamente, sem path traversal encontrado +**Severidade:** INFORMATIVO +**Componente:** `backend/src/services/BackupService.ts:73-74` +**Evidência:** `filename = device.name.replace(/[^a-z0-9]/gi, '_')`; `file_path` sempre construído com `path.join(BACKUPS_DIR, String(device.id), filename)`, `device.id` é `SERIAL` numérico. Endpoints de download/preview/restore buscam `file_path` do banco por `id` numérico, nunca aceitam path do cliente. +**Status:** FALSE_POSITIVE + +### SEC-014 — RBAC de backend (rotas) consistente com o esperado +**Severidade:** INFORMATIVO +**Evidência:** todas as 28 rotas verificadas têm `router.use(requireAuth)` (ou, no caso de `oidc.ts`, `requireAuth`/`requireAdmin` por rota, corretamente deixando só `/status`, `/login`, `/callback` públicos). Endpoints administrativos sensíveis (`users`, `oidc/config`, `audit-log`, `cert/regenerate`, `credential-presets`, `maintenance-windows`) usam `requireAdmin`. Autoproteção contra auto-rebaixamento/auto-exclusão de admin implementada em `routes/settings.ts`. +**Status:** FALSE_POSITIVE (sem achado — RBAC de rota está correto) + +## Achados por severidade +- Crítico: 0 +- Alto: 1 (SEC-002) +- Médio: 4 (SEC-001, SEC-003, SEC-004, SEC-005) +- Baixo: 5 (SEC-006, SEC-007, SEC-008, SEC-009, SEC-010) +- Informativo: 5 (SEC-011 a SEC-014, incl. 2 achados positivos) + +## Principais 10 achados +1. SEC-002 — Sites não são fronteira de autorização (ALTO para multi-tenant). +2. SEC-005 — Change Guard cobre só um subconjunto pequeno de operações de escrita. +3. SEC-001 — Sessões JWT sem revogação server-side. +4. SEC-003 — RouterOS API-SSL sem verificação de certificado (`rejectUnauthorized: false`). +5. SEC-004 — Chaves de criptografia default como fallback de decrypt permanente. +6. SEC-006 — Injeção potencial de comando RouterOS via `ssh_username`. +7. SEC-009 — DoS moderado transitivo em `qs`/`express` (não corrigido por instrução da missão). +8. SEC-007/SEC-008 — Supply chain: imagens sem pinning, `npm install` em vez de `npm ci`. +9. SEC-010 — Rate limit de login só por IP. +10. SEC-012/SEC-013/SEC-014 — Pontos fortes confirmados: criptografia de credenciais, sanitização de path de backup, RBAC de rota consistente. + +## Credenciais RouterOS +**Avaliação: aceitável com ressalva.** Armazenamento em repouso é sólido (AES-256-GCM, IV único, tag autenticada, chave fora do banco, rotação suportada). A ressalva é em trânsito: TLS sem verificação de certificado (SEC-003) expõe a credencial a MITM na rede local. Não há exposição das credenciais em API, logs (`logSafe`/`safeClientError` fazem redação deliberada) ou frontend (`maskedConfig` para OIDC, sem exposição de `*_encrypted` observada nas rotas revisadas). + +## RBAC +**Avaliação: seguro dentro de um único tenant, inseguro para multi-cliente.** A separação admin/operator/viewer é aplicada corretamente no backend (não só na UI) e resiste a manipulação direta de endpoint. A falha estrutural é SEC-002: não existe isolamento por site/cliente — inaceitável se a GTI planeja hospedar múltiplos clientes finais na mesma instância antes de implementar `user_sites`. + +## Operações remotas +**Avaliação: seguro com uma ressalva de TLS.** Terminal SSH interativo tem controle de papel (admin/operator), auditoria e rate limit por usuário. RouterOS API usa protocolo binário estruturado (não concatenação de string), reduzindo risco de injeção clássica. A ressalva é SEC-003 (TLS sem verificação). + +## Change Guard +**O que realmente protege:** mudanças que passam pelo fluxo de "preflight + apply analisado" em `devices.ts`, com snapshot no dispositivo e rollback automático via scheduler RouterOS se a verificação de conectividade pós-mudança falhar. +**O que NÃO protege:** a maioria das edições diretas de firewall/NAT/rotas/VLAN/interfaces, execução de comandos em massa (`commands.ts`) e o terminal interativo — nenhum desses passa por `withSafeApply`. + +## Backups +**Estado: seguro para o escopo atual.** Nomes de arquivo sanitizados, paths nunca vêm do cliente, autorização de escrita (`requireWrite`) presente em criar/restaurar/excluir. Sem criptografia em repouso do arquivo `.rsc` em si (fica em texto plano no volume `backups_data`) — aceitável para DEV, a avaliar antes de integrar com SFTP GTI (fora do escopo desta missão). + +## Docker +**Estado: funcional, com dívida de reprodutibilidade.** Non-root user no backend, capability mínima necessária (`NET_RAW` só para arp-scan/ping), healthchecks presentes. Pontos a melhorar: pinning de imagens (SEC-007) e `npm ci` (SEC-008). + +## Dependências +**Estado: aceitável.** Backend: 3 vulnerabilidades moderadas transitivas (SEC-009), sem crítico/alto. Frontend: 0 vulnerabilidades em dependências de produção. Nenhuma atualização automática foi aplicada, conforme instrução da missão. + +## Testes +``` +BACKEND TESTS: 545/545 (543 originais + 2 de auditoria, SEC-001/SEC-002) +FRONTEND TESTS: 56/56 +TYPECHECK: PASS +LINT: PASS (0 erros; 270 warnings backend / 108 frontend, security/detect-object-injection e afins — nenhum classificado como vulnerabilidade real nesta auditoria; ver observação abaixo) +BUILD: PASS +``` + +**Nota sobre os warnings do `eslint-plugin-security`:** os 378 warnings (270+108) foram amostrados por arquivo (`rfSpectrum.ts`, `serverArp.ts`, `secrets.ts`, `wifiChannels.ts`, `topologyLayout.ts` entre outros). Nenhum ponto analisado usa índice vindo diretamente de entrada HTTP não validada para acessar um objeto sensível (a maioria é iteração sobre arrays/enums internos, falso positivo típico da regra `detect-object-injection`). Classificação: **falso positivo majoritário** — não foi encontrada uma cadeia de exploração real amostrando os arquivos mais citados pela ferramenta. Não corrigidos mecanicamente, conforme instrução da missão. + +## Primeira RB GTI read-only +**AUTORIZADA PARA PRIMEIRA RB GTI READ-ONLY: SIM** + +Para uma **única** RouterBOARD, em modo **read-only** (sem escrita, sem Change Guard necessário, sem terminal), com um único usuário/tenant, a base é segura o suficiente: credenciais cifradas corretamente, RBAC de rota correto, auditoria presente. As ressalvas (TLS sem verificação — SEC-003, sessões sem revogação — SEC-001) são aceitáveis para um piloto controlado e monitorado. + +## Correções obrigatórias antes da primeira RB **de escrita** ou multi-cliente +1. **SEC-002** — implementar RBAC por site antes de conectar RBs de mais de um cliente final na mesma instância. +2. **SEC-005** — decidir explicitamente (e documentar para os operadores GTI) quais operações de escrita ficam sem rede de segurança do Change Guard, ou estender `withSafeApply` a firewall/rotas antes de liberar escrita irrestrita em produção. +3. **SEC-003** — avaliar fixação de fingerprint TLS antes de operar RBs fora de uma rede de gestão confiável. + +## Documentação operacional +`docs/SISTEMA_ATUAL.md` foi atualizado (seções 11 e 22) para refletir SEC-001, SEC-002, SEC-003 e SEC-005 como fatos comprovados sobre o sistema atual. + +## Git +- Commit desta missão: ver `git log -1` após o commit de auditoria (arquivos: este relatório, teste `middleware/__tests__/auditFindings.test.ts`, atualização de `docs/SISTEMA_ATUAL.md`). +- Nenhuma refatoração ampla foi feita; nenhuma correção de vulnerabilidade foi aplicada ao código de produção (todos os achados ficam como `OPEN`/`ACCEPTED`, conforme regra da missão — só se corrige o que é necessário para provar/criar o teste). +- Worktree limpo ao final. + +## VEREDITO FINAL + +# APROVADO COM CORREÇÕES OBRIGATÓRIAS + +A arquitetura é sólida e o nível de engenharia é notavelmente maduro para uma base open source (comentários de causa-raiz, testes extensivos, defesa em profundidade em vários pontos, Change Guard bem desenhado onde é usado). Não há achado CRÍTICO. Mas existe um achado ALTO (SEC-002) que é estrutural para o modelo de negócio pretendido pela GTI (múltiplos clientes) e um MÉDIO (SEC-005) que muda a expectativa operacional sobre o que o Change Guard cobre. Nenhum dos dois inviabiliza a base — ambos são corrigíveis sem trocar de projeto — mas devem ser resolvidos antes de qualquer RB de escrita ou multi-cliente real. + +## Próxima missão recomendada +**MISSÃO 02 — RBAC por site (`user_sites`) + extensão do Change Guard aos endpoints de firewall/rotas mais usados**, antes de iniciar qualquer integração GTI (SFTP, GLPI, Slack) ou conectar a primeira RouterBOARD real em modo de escrita. diff --git a/docs/SISTEMA_ATUAL.md b/docs/SISTEMA_ATUAL.md index e9dc47c..b42d187 100644 --- a/docs/SISTEMA_ATUAL.md +++ b/docs/SISTEMA_ATUAL.md @@ -46,7 +46,9 @@ React 19 + Vite + TailwindCSS, servido como estático pelo nginx. Build gera chu Node.js/TypeScript, Express, autenticação JWT, TOTP opcional, OIDC opcional, coletor NetFlow/IPFIX na porta 2055/udp, backups, SSH (ssh2) para RouterOS. ## 11. Autenticação -JWT (`JWT_SECRET`), senha com bcryptjs, suporte a TOTP e OIDC. Usuário admin default criado na migration inicial (`admin/admin`) — **a senha padrão DEVE ser trocada imediatamente após o primeiro login** (feito nesta instalação DEV; ver seção 12 do CLAUDE.md). +JWT (`JWT_SECRET`, HS256, expiração 24h), senha com bcryptjs, suporte a TOTP e OIDC. Usuário admin default criado na migration inicial (`admin/admin`) — **a senha padrão DEVE ser trocada imediatamente após o primeiro login** (feito nesta instalação DEV; ver seção 12 do CLAUDE.md). + +RBAC: 3 papéis globais (`admin`/`operator`/`viewer`), aplicados corretamente no backend em toda rota (ver `docs/AUDITORIA_BASE_2GT.md`). **Não existe isolamento por site/cliente** — o filtro `X-Site-Id` é apenas de exibição; qualquer usuário autenticado enxerga todos os dispositivos de todos os sites se omitir o header (achado SEC-002). Sessões JWT não têm revogação server-side: trocar senha, desabilitar TOTP ou remover um usuário não invalida tokens já emitidos até expirarem (achado SEC-001). ## 12. Ambiente DEV - Diretório: `D:\PROJETOS\MIKROTIK RMM\mikrormm` (Windows 11, Git Bash/MINGW64) @@ -112,7 +114,11 @@ Backend expõe rotas de backup de dispositivos (`backups_data`); backup do próp - Secrets DEV gerados aleatoriamente (DB_PASSWORD 32c, JWT_SECRET 64c hex, ENCRYPTION_KEY 32c, INFLUXDB_TOKEN 64c hex, INFLUXDB_ADMIN_PASSWORD 24c) - Senha padrão do admin alterada (ver `.secrets-local/dev-admin-password.txt`, fora do Git) - Certificado HTTPS self-signed gerado pelo nginx (`nginx/entrypoint.sh`) — adequado só para DEV -- Lint de segurança (`eslint-plugin-security`) roda no baseline com 270 (backend) + 108 (frontend) warnings, 0 erros — não corrigido nesta missão (fica para auditoria) +- Lint de segurança (`eslint-plugin-security`) roda no baseline com 270 (backend) + 108 (frontend) warnings, 0 erros — auditados na Missão 01 (`docs/AUDITORIA_BASE_2GT.md`), majoritariamente falso positivo +- Credenciais RouterOS cifradas em repouso com AES-256-GCM, chave fora do banco (`SECRETS_DIR/secrets.json`), IV único por operação, tag autenticada — design confirmado correto na auditoria +- Conexão RouterOS API-SSL usa `rejectUnauthorized: false` (sem verificação de certificado) — suscetível a MITM na rede local (achado SEC-003, OPEN) +- Change Guard (dead-man-switch) só cobre o fluxo de "preflight + apply analisado" em `devices.ts`; a maioria das edições diretas de firewall/rotas/VLAN, comandos em massa e o terminal interativo não passam por ele (achado SEC-005, OPEN) +- Ver `docs/AUDITORIA_BASE_2GT.md` para a lista completa de achados, severidades e status ## 23. Integrações atuais Nenhuma integração externa real conectada (GLPI, Slack, SFTP, RouterBOARD real) — fora do escopo desta missão.