docs: record upstream security audit (Mission 01)

- docs/AUDITORIA_BASE_2GT.md: full audit of upstream 1a4b635 (v0.24.5-beta)
  - 0 critical, 1 high (SEC-002: no per-site RBAC), 4 medium, 5 low, 5 informational
  - Verdict: APROVADO COM CORREÇÕES OBRIGATÓRIAS
  - First read-only RB: authorized (SIM)
- test: add regression coverage proving SEC-001 (no JWT revocation) and
  SEC-002 (site scoping has no per-user authorization component)
- docs/SISTEMA_ATUAL.md: record confirmed facts (RBAC scope, Change Guard
  coverage, RouterOS TLS verification) surfaced by the audit

No production code changed — audit-only per mission scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Gustavo
2026-09-08 18:32:03 -03:00
co-authored by Claude Sonnet 5
parent 7a4979e0e1
commit 5b926d637b
3 changed files with 286 additions and 2 deletions
@@ -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
});
});
+222
View File
@@ -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.
+8 -2
View File
@@ -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. 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 ## 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 ## 12. Ambiente DEV
- Diretório: `D:\PROJETOS\MIKROTIK RMM\mikrormm` (Windows 11, Git Bash/MINGW64) - 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) - 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) - 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 - 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 ## 23. Integrações atuais
Nenhuma integração externa real conectada (GLPI, Slack, SFTP, RouterBOARD real) — fora do escopo desta missão. Nenhuma integração externa real conectada (GLPI, Slack, SFTP, RouterBOARD real) — fora do escopo desta missão.