Files
MIKROTIK-RMM/docs/AUDITORIA_BASE_2GT.md
GustavoandClaude Sonnet 5 5b926d637b 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>
2026-09-08 18:32:03 -03:00

223 lines
23 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.