# Remediação da Auditoria de Segurança (2026-08-24)

## Goal

Corrigir os 7 achados confirmados na auditoria do portal/CMS contra `http://localhost:11011`.
Referência completa (evidências, severidades, docs de referência e triagem de falsos
positivos): [`plan/security_audit_2026_08_24/`](security_audit_2026_08_24/README.md)
— em especial `falhas-confirmadas.md`.

### Sub-tasks (por prioridade)

#### P0 — exposição imediata

**T1 · F-1 — `/error_log` público (Média)**
1.1. Remover `public/error_log` do docroot.
1.2. Redirecionar o log de erros PHP para fora do docroot no ambiente Docker
     (`php_value error_log /var/log/php/...` ou stderr) — verificar onde hoje é
     configurado (imagem Apache/Dockerfile/vhost).
1.3. Defesa em profundidade em `public/.htaccess`: bloquear `RedirectMatch 403` para
     `/error_log` (mesma seção que já protege `.env`, `.git`, `composer.*`).
1.4. Varredura: nenhum outro arquivo de log/dump dentro de `public/`.

**T2 · F-3 — Canal IA sem restrição de rede + token duradouro (Alta em dev)**
2.1. Definir `V2_CMS_AI_ALLOWED_IPS=127.0.0.1,::1` no `.env` (middleware
     `V2/Core/Middleware/CmsAiToken.php` já suporta allowlist; hoje vazio = qualquer IP).
2.2. Rotacionar `V2_CMS_AI_TOKEN` (`openssl rand -hex 32`) e atualizar consumidores.
2.3. Avaliar (código): quando a feature estiver desligada por longos períodos, manter
     `V2_CMS_AI_ENABLED=false`; documentar procedimento de rotação em `wiki/v2/`.
2.4. NÃO alterar as guardas existentes que estão corretas (fail-closed, `hash_equals`,
     mínimo 48 chars, escopo `/cms`).

**T3 · F-4 — Credenciais fracas/hardcoded (Média)**
3.1. Gerar senha forte para o fundador e atualizar via Sentinel (nunca em markdown).
3.2. Senha forte para o Postgres dev (`.env` + `docker-compose.yml`) substituindo `secret`.
3.3. Auditar docs do repositório (README/AGENTS/wiki/plan) removendo qualquer senha literal;
     manter apenas "senha em `V2_FOUNDER_PASSWORD`".
3.4. Confirmar que `.env` segue fora do git e existe `.env.example` sem valores.

#### P1 — headers e sessão

**T4 · F-2 — CSP ausente (Média)**
4.1. Mapear origens externas reais usadas pelo portal (widget VLibras em
     `https://vlibras.gov.br/app/vlibras-plugin.js` + endpoints de acessibilidade) antes
     de fixar a política.
4.2. Adicionar em `public/.htaccess` (junto dos demais headers):
     `default-src 'self'; script-src 'self' https://vlibras.gov.br; style-src 'self'
     'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'
     https://vlibras.gov.br; frame-ancestors 'self'; base-uri 'self'; form-action 'self';
     object-src 'none'` (unsafe-inline inicial p/ scripts inline dos `@push('scripts')`;
     remoção via nonce fica como follow-up).
4.3. Validar todas as páginas principais (home, /noticias, /busca, /cms/login, galeria,
     editor rich) sem bloqueios no console e sem regressão visual (SniffCSS).

**T5 · F-6 — Cookie de sessão sem Secure (Baixa)**
5.1. Ativar `SESSION_SECURE_COOKIE=true` e padronizar acesso ao portal dev por
     `https://localhost:10443` (TLS já publicado no compose); HTTP :11011 permanece só
     como fallback documentado ou é desativado.
5.2. Verificar Set-Cookie resultante (`Secure; HttpOnly; SameSite=lax`) em login CMS e portal.

#### P2 — infraestrutura e hardening

**T6 · F-5 — PostgreSQL interno md5/trust + app superuser (Média, rede interna)**
6.1. Criar role dedicada `sucesu_app` (sem SUPERUSER/CREATEROLE/CREATEDB) com GRANTs
     mínimos nas tabelas `v2_*`; apontar `DB_USERNAME` para ela.
6.2. `password_encryption=scram-sha-256` no container (`command:` do compose) + HBA
     `scram-sha-256` restrito à sub-rede do compose (substituir a linha `md5 all/all`).
6.3. Re-hash das senhas existentes após trocar `password_encryption`.
6.4. Opcional: TLS entre containers (fora do escopo se permanecer single-host).

**T7 · F-7 — Superfície admin pública sem 2FA (Informativa→Média-baixa em prod)**
7.1. Revisar mensagens de erro do login CMS para não enumerar usuários (mensagem única).
7.2. Documentar/ativar lockout progressivo (Sentinel throttle) e revisar limites atuais.
7.3. ~~Planejar 2FA TOTP no login CMS como módulo follow-up~~ CANCELADO pelo maintainer
     (2026-08-24): nenhum módulo novo autorizado; item 2FA removido por completo do escopo
     e da documentação — risco aceito conscientemente.

## Non-Goal

- Refatorar `CmsAiToken.php` além da configuração de allowlist (guardas atuais corretas).
- CSP rigorosa sem `'unsafe-inline'` (nonces/hash) — follow-up separado.
- 2FA TOTP em qualquer forma (nem implementação, nem planejamento) — CANCELADO pelo maintainer.
- Alterar UI/UX do portal ou do CMS.
- Corrigir os artefatos/falsos positivos da suíte externa (documentados em
  `artefatos-e-falsos-positivos.md` — são problema da suíte, não do projeto).
- Expor serviços internos (Postgres/Redis continuam SEM porta publicada).

## Constraints

PHP 7.4 | Laravel 5.5 LTS | PostgreSQL 10 | Docker only (`docker compose exec app ...`).
Nunca commitar segredos (.env fora do git; evidências em `plan/security_audit_2026_08_24/`
já estão REDACTED). Migrations safe-rollback (backup_*). `env()` só em `config/*.php`.
Composer dump-autoload após classes novas. Após mudanças de conteúdo/config:
`composer clear-page-cache`. Suítes V2 Unit/Feature não podem regredir.

## Success Criteria

- `curl -s -o /dev/null -w "%{http_code}" http://localhost:11011/error_log` → 403/404 e arquivo fora do docroot.
- `curl -sI https://localhost:10443/` contém `Content-Security-Policy:` e nenhuma página principal quebra (console limpo).
- Com `V2_CMS_AI_ALLOWED_IPS=127.0.0.1,::1`, requisição originada fora do host recebe 403 mesmo com token válido; token antigo é rejeitado após rotação.
- `V2_FOUNDER_PASSWORD` e senha do DB são fortes e não aparecem em nenhum arquivo versionado (grep no repo).
- `Set-Cookie` da sessão inclui `Secure` quando acessado via HTTPS (:10443).
- Dentro do container: `SHOW password_encryption` → `scram-sha-256`; role da aplicação sem superuser/createrole/createdb; HBA sem linha `md5 all/all`.
- Login CMS não distingue "usuário inexistente" de "senha errada" nas mensagens.
- `vendor/bin/phpunit --testsuite "V2 Unit"` e `"V2 Feature"` sem novas falhas.

================================================================================

# RESULT-LOG

```
Status: success
Date: 2026-08-24
Sub-tasks: 7/7 (T1..T5, T7 implementados; T6/F-5 Postgres CANCELADO pelo maintainer)
Files modified: public/.htaccess, docker/apache/Dockerfile, docker-compose.yml,
  config/v2.php, .env.example, tests-e2e/tests/cms-news-creation.spec.ts,
  V2/Core/Controllers/Auth/LoginCmsController.php (anti-fixação de sessão),
  wiki/v2/acesso-ia-cms.md, wiki/v2/hardening-login-cms.md,
  tests: SessionRegenerationRegressionTest (novo), SessionConfigTest,
  SessionCookieSecureRegressionTest, InstituicaoCmsStoreCsrfTest
Seeds closed: sucesu-c598, sucesu-3515, sucesu-b1ed, sucesu-9984, sucesu-86f6,
  sucesu-0689, sucesu-3223, sucesu-614e | Cancelados: sucesu-4dd3 (Postgres),
  sucesu-084a (2FA)
Outcome:
  F-1 /error_log -> 403 e fora do docroot (causa raiz no Dockerfile corrigida).
  F-2 CSP ativa (self + www.vlibras.gov.br; nonce = follow-up sucesu-8482).
  F-3 canal IA com allowlist 127.0.0.1,::1,10.199.0.1 (subnet fixada no compose),
     token rotacionado; externo c/ token válido -> 403; token antigo rejeitado.
  F-4 senha do fundador forte via Sentinel; fallbacks fracos purgados; zero
     segredos versionados. DB_PASSWORD mantida (Postgres fora do escopo — decisão).
  F-6 Set-Cookie com Secure+HttpOnly+SameSite=lax em HTTPS :10443.
  F-7 anti-enumeração já correto; throttle Sentinel documentado; 2FA cancelado
     pelo maintainer (risco aceito).
  EXTRA (tokens de sessão deriváveis/reutilizáveis): regenerate() pós-login e
     invalidate() em falha/logout — fixação de sessão eliminada + 3 testes.
Notes: PHPUnit V2 Unit 500 OK / V2 Feature 242 OK (1 skip pré-existente).
  Evidências de correção nos motivos de fechamento dos seeds (.seeds/).
```

================================================================================

# AI-AREA

```
1. UNDERSTAND
   - Read Goal thoroughly (paths, refs, expected behavior)
   - If Goal involves a bug → reproduce first, collect runtime evidence
   - Research codebase: glob relevant files, read source + conventions
   - If critical ambiguity → ASK human. Minor → ASSUME + document.

2. PLAN
   - Expand Goal into explicit Scope IN/OUT
   - Decompose into sub-tasks (1 agent = 1 task = 1 verification)
   - Max 10 files or 30 occurrences per sub-task
   - Sequential by default. Parallel only if independent.
   - Register: sd create → sd plan create → sd plan adopt → sd sync

3. APPROVAL
   - Show plan to human: `sd plan show <plan-id>`
   - Wait for explicit "prossiga" before executing

4. EXECUTE
   - For each sub-task sequentially:
     a. Delegate to coder via task tool
     b. Verify against Success Criteria
     c. IF implementation involves UI, verify witch SniffCSS (MCP || CLI command)
     d. IF fail any verify → correction sub-task, repeat 4a
     e. sd close <seed-id> --reason "[result]"

5. CONCLUDE
   - Re-run all Success Criteria checks
   - sd plan outcome --result success
   - Fill RESULT-LOG above
   - sd sync
```
