---
id: REIMPL-RISCOS
tipo: reimplementacao
onda: 5
status: concluido
unidades: []
fontes:
  - V2/
  - app/
  - config/
  - public/
  - composer.lock
  - docker-compose.yml
  - .env.example
depende_de: [REIMPL-ORDEM, REIMPL-PARIDADE, CORE-AUTH, CORE-RUNTIME]
consumidores: [equipe-de-reconstrucao, security-reviewer, qa]
criticidade: alta
dono: agente-reimplementacao
---

## Efeito

Consolida os riscos que a reconstrução herda se copiar o legado e os riscos que ela cria
se divergir dele. Cada risco tem evidência citada, efeito observável e uma regra de
reconstrução. O resultado esperado é que a decisão “reproduzir” ou “corrigir” seja
explícita — nunca implícita no código.

Duas categorias:

- **R-01 … R-14** — segurança, derivados da revisão `_qa/security-g1.md` e confirmados
  contra os documentos de unidade.
- **R-15 … R-24** — técnicos e de fidelidade documental (execução de código, escaping,
  encoding, tokens, schema órfão, tags, assets, versões, deriva de contrato).

## Abordagem

### 1. Registro de riscos de segurança

| ID | Risco | Sev. | Evidência | Efeito no legado | Regra de reconstrução |
|---|---|---|---|---|---|
| R-01 | Gallery CMS sem autenticação e com CSRF isento | Crítica | `_qa/security-g1.md:20-46`; `03_servicos-ui/gallery/spec.md:107,126,548` | anônimo lista, baixa, gira, arquiva, apaga e envia mídia; `svg` aceito | `CmsAuthenticate` + RBAC de módulo + CSRF em todas as 7 rotas; `owner_module` derivado do servidor; teste negativo CT-ROUTES-04/CT-TOKENS-03 |
| R-02 | Guarda de produção do canal IA compara `production`, mas o compose usa `prod` | Alta | `_qa/security-g1.md:48-63`; `01_arquitetura/11-satelites-build-config.md:98` | com `APP_ENV=prod` e `V2_CMS_AI_ENABLED=true` o canal autentica | falhar fechado por padrão (`enabled=false`, token ausente); allow-list de IP como defesa secundária; teste com `prod` |
| R-03 | `TrustProxies='*'` torna allow-list de IP spoofável | Alta | `_qa/security-g1.md:64-78`; `01_arquitetura/04-middlewares-seguranca.md:486` | `X-Forwarded-For` forjado passa em `allowed_ips` e no throttle | confiar só em CIDRs do proxy real; remover headers do cliente; classificar IP como defesa secundária |
| R-04 | Reset de tema CMS sem guarda de suporte | Média | `_qa/security-g1.md:79-95`; `05_modulos/Support/StructuralLandingEditor/spec.md:1000` | qualquer usuário autenticado sobrescreve marca, logo e favicon | guarda por action + `active_pattern` em array; teste CT-PERM-07 |
| R-05 | `consumer` da API de importação validado no controller, não no middleware; chave estática ignora o controle | Média | `_qa/security-g1.md:96-109`; `05_modulos/Support/NoticiasImport/contrato-api.md:664` | chave estática cai no `consumer` default `PE` | validar `consumer` na autenticação; chave estática, se existir, recebe escopo explícito; teste CT-IMPORT-02 |
| R-06 | SupportConfigs escapa do RBAC de rota por `active_pattern` com `|` | Média | `_qa/security-g1.md:110-122`; `_qa/review-g3.md:91-107` | `Str::is` trata `|` como literal; grupo nunca casa e a proteção fica só por action | patterns em array; guarda em todas as actions, inclusive reset; teste de URL direta |
| R-07 | Dashboard com `v2_role_id=null` responde 200 (fail-open do RBAC) | Média | `_qa/security-g1.md:123-134`; `PC-CORE-AUTH-28` | sidebar vazia, mas `/cms`, `/cms/search` e `/cms/conexao-tags` acessíveis | decidir explicitamente: manter 200 documentado ou negar; teste de matriz |
| R-08 | `last_login` é escrito pelo Sentinel antes dos gates | Baixa | `_qa/security-g1.md:135-146` | login negado ainda atualiza a coluna | gravar só após autorização, ou documentar a semântica de auditoria; teste CT-PERM-05 |
| R-09 | Módulo desativado em rota CMS de prefixo composto retorna 404, não 302 | Baixa | `_qa/security-g1.md:147-157` | `cms/noticias` não redireciona; `cms` redireciona | unificar em 404 ou 302 por decisão; teste de cada prefixo |
| R-10 | Versão do Sentinel divergente (2.0.16 documentada, 2.0.18 no lock) | Baixa | `_qa/security-g1.md:158-166`; `01_arquitetura/11-satelites-build-config.md:553` | TTL de sessão/throttle sem contrato explícito | fixar versão ou substituir auth e declarar TTL/cookie; teste de sessão |
| R-11 | Login AJAX retorna 500 antes do handler de flash | Baixa | `_qa/security-g1.md:167-176` | `ThrottlingException`/`NotActivatedException` em AJAX viram 500 JSON genérico | tratar exceções antes do fallback AJAX; teste de login AJAX |
| R-12 | Canal IA não revalida role/ativação e impersona o suporte por default | Média | `_qa/security-g1.md:177-190` | usuário alvo desativado continua operando; alvo default é o suporte com bypass total | usuário de máquina dedicado, menor privilégio, revalidação por request, `enabled=false` por default |
| R-13 | Token por query pode vazar em log de outro middleware | Baixa | `_qa/security-g1.md:191-201` | `v2_ai_token` aparece em `fullUrl()` quando o RBAC nega | `allow_query=false`; logar `path()` sem query; nunca logar header |
| R-14 | Bearer regional fora do inventário; TTL fixo em 365 dias | Baixa | `_qa/security-g1.md:202-214`; `05_modulos/Conexoes/UnidadeRegional/spec.md:61,761` | `config('v2.unidade_regional.api_token_ttl_days')` não existe; sem throttle em GET/DELETE | declarar TTL configurável; throttle em todos os verbos; rotação/revogação testadas |

### 2. Registro de riscos técnicos

| ID | Risco | Sev. | Evidência | Efeito | Regra de reconstrução |
|---|---|---|---|---|---|
| R-15 | `eval()` de bloco no SLE | Crítica | `05_modulos/Support/StructuralLandingEditor/spec.md:32,315,486,999` | qualquer conta de suporte executa PHP no request do portal; não há CSP nonce nem saneamento | não usar `eval`; renderizar por widgets/AST; se HTML cru for requisito, sanitizar com allow-list e servir em sandbox de origem separada |
| R-16 | `{!! content !!}` sem escaping (BasicPage) e HTML cru em SLE/LSE | Alta | `05_modulos/Layouts/BasicPage/spec.md:88,765`; `05_modulos/Layouts/BasicPage/runtime-e-seo.md:185-189`; `05_modulos/Support/StructuralLandingEditor/spec.md:926`; `01_arquitetura/07-controllers-core.md:318` | XSS armazenado por autor de CMS; `<script>` no conteúdo | política de sanitização no save e no render; CSP com nonce; teste de payload `<script>`/`onerror` |
| R-17 | `metadata` duplamente codificado no `store()` de BasicPage | Média | `05_modulos/Layouts/BasicPage/spec.md:242,387,409` | primeira edição/SEO pode falhar ou cair em default; `metadata` vira string JSON dentro de JSON | gravar array (cast serializa) ou remover o cast; migrar linhas existentes com decodificação única; teste A2 |
| R-18 | Tokens e TTL sem contrato uniforme | Alta | `01_arquitetura/03-auth-rbac-sentinel.md:493,767`; `01_arquitetura/04-middlewares-seguranca.md:484-486,507`; `01_arquitetura/09-cache-csp-minify-maintenance.md:372-374`; `01_arquitetura/11-satelites-build-config.md:89` | token de IA fixo e sem TTL; chave estática ignora TTL/escopo/IP; TTL do cache em minutos pode virar segundos; sessão usa defaults do pacote | definir uma tabela de TTL por credencial; implementar expiração/rotação/revogação; teste CT-TOKENS e CT-CACHE-04 |
| R-19 | PHP 7.4 / Laravel 5.5 / Postgres 10 / PHPUnit 6 em EOL | Alta | `01_arquitetura/11-satelites-build-config.md:226,344-345,522,553` | sem patches; comportamento acoplado (TTL em minutos, `str_slug`, `assertStringContainsString`); atualizar quebra contratos | **resolvido por decisão**: alvo PHP 8.4/Laravel 12/PG 18/PHPUnit 12 (`pilha-alvo-public-v7.md` §2); manter contratos observáveis, não APIs de framework; testar TTL, slug e auth na pilha nova |
| R-20 | Tabelas órfãs e backups no schema | Média | `_indices/tabelas.csv:12,16,20,37,39`; `01_arquitetura/12-migracoes-inventario.md:390-407` | `v2_contato_configs` sem consumidor; `v2_chapters` só existe como `backup_*`; `v2_news_approvers`/`v2_news_configs` removidas; rollback acumula backups | não criar órfãs na reconstrução; migrar dados úteis e descartar o resto com registro; `migrate:fresh --seed` não pode gerar `backup_*` (`PC-CORE-DATA-23`) |
| R-21 | Dualismo de catálogos de tags | Média | `01_arquitetura/05-persistencia-uuid-migracoes.md:184,289`; `_indices/tabelas.csv:12-13,19` | `v2_associado_tags` e `v2_conexao_tags` coexistem; pivôs novos não recebem tags; “geral” é reservada | escolher um catálogo; migrar os dois pivôs legados; manter “geral” como invariante; teste de unicidade por slug |
| R-22 | Assets sem build e bundle versionado | Média | `01_arquitetura/11-satelites-build-config.md:41,57,230,268,519` | não há `package.json`/Vite; CSS/JS editados à mão; único build é `build-bundle.sh`; nenhuma lib via CDN | se adotar build, gerar manifesto e reescrever todos os `asset()`; preservar CSP e ordem de carregamento; teste de bundle byte a byte (`PC-CORE-RUNTIME-22`) |
| R-23 | Deriva entre documento e código em pontos usados como aceite | Média | `_qa/review-g2.md:76-153`; `_qa/review-g3.md:49-189` | 404 documentado que é 500, ranking SEO irreprodutível, favicon 500 que é 302, LSE 21 vs 36 | usar a versão corrigida dos PCs (ver `paridade.md` §4); nunca escrever teste a partir de número não reconciliado |
| R-24 | Contagem de rotas não reconciliada (277 indexadas × 308 `Route::*`) | Baixa | `_indices/rotas.csv`; `PLANO.md:83,321` | rota não documentada pode passar no gate | conciliar antes de aceitar A1; rota nova exige linha no CSV |

### 3. Como cada risco vira teste

| Risco | Teste mínimo | Camada |
|---|---|---|
| R-01 | `GET /cms/v2/gallery` sem sessão → 401; `POST /rotate` sem `_token` → 419 | A1 |
| R-02, R-03 | canal IA com `APP_ENV=prod` e `X-Forwarded-For` forjado → 401/403 | A1 |
| R-04, R-06 | reset de tema com perfil comum → 302/403; URL direta de SupportConfigs → 302 | A1 |
| R-05, R-14 | `consumer` divergente → 403; token regional expirado/revogado → 401 | A1, A3 |
| R-07, R-08 | matriz de role nulo; `last_login` antes/depois do gate | A1, A2 |
| R-11, R-12 | login AJAX com throttle; canal IA com usuário desativado | A1, A3 |
| R-15, R-16 | bloco com `<?php system(...) ?>` não executa; `<script>` é sanitizado | A3, A4, A5 |
| R-17 | `store` grava `metadata` uma única vez; leitura devolve array | A2 |
| R-18 | TTL/expiração de cada credencial; cache expira em 300 s | A1, A3 |
| R-19 | suíte roda na pilha-alvo; TTL e slug cobertos | A3 |
| R-20, R-21 | `migrate:fresh --seed` sem `backup_*`; uma única tag por slug | A2 |
| R-22 | diff vazio ao regerar o bundle; ordem de `<link>`/`<script>` | A4 |
| R-23, R-24 | PCs corrigidos verdes; tabela de rotas sem delta | A1 |

### 4. Matriz de decisão: reproduzir ou corrigir

| Comportamento | Reproduzir? | Por quê |
|---|---|---|
| Status/mensagens literais de erro de negócio | Sim | são contrato de UI e de testes |
| Ordem `module.active` antes de `CmsAuthenticate` | Sim, com cautela | é observável (404 para módulo off) e evita enumerar rotas; não expõe dados |
| Rotas de serviço fora da pilha CMS | **Não** | é a causa de R-01; a correção não muda URL nem payload |
| Reset de tema sem guarda | **Não** | não é usado por nenhum fluxo legítimo; a guarda preserva o contrato |
| `eval` de bloco | **Não** | execução arbitrária; substituível por widgets |
| `{!! content !!}` em BasicPage | **Não** sem sanitização | o contrato é “HTML do autor”, não “qualquer script” |
| Duplo-encode de `metadata` | **Não** | é bug; a correção é transparente para o leitor |
| Tabelas órfãs e `backup_*` | **Não** | não têm consumidor; migrar só o que tem dado útil |
| Dois catálogos de tags | **Não** | gera divergência; escolher um e migrar |
| TTL de 5 minutos no cache | Sim (semântica) | é contrato observável (`PC-CORE-RUNTIME-09`); mudar a unidade sem mudar o valor é bug |
| Assets sem build | Decisão explícita | adotar build é permitido, mas quebra URLs e cache-busting; exige reescrita de todos os `asset()` |
| PHP 7.4/Laravel 5.5 | **Não** | EOL; contratos devem ser mantidos na pilha nova |

## Particularidades

1. **Os riscos não são homogêneos.** R-01, R-15 e R-16 são exploráveis por usuário
   autenticado ou anônimo; R-09, R-10 e R-11 são de fidelidade. Tratá-los na mesma fila
   atrasa o que importa.
2. **A correção de R-01 muda o contrato de resposta.** Hoje o anônimo recebe 200; a
   reimplementação deve responder 401/403/419. Isso é intencional e está registrado em
   `PC-GALLERY-10`.
3. **R-15 e R-16 se somam.** SLE executa PHP (`eval`) e BasicPage ecoa HTML sem escape. Um
   reconstruction que copie apenas um dos dois mantém a superfície de XSS/RCE.
4. **R-17 é silencioso.** O duplo-encode não gera erro visível; só aparece quando o
   formulário tenta ler `metadata` como array. Por isso o teste precisa inspecionar o valor
   bruto no banco.
5. **R-18 é uma família, não um item.** Sessão Sentinel, token de IA, chave interna,
   bearer regional e cache têm TTLs diferentes; unificá-los quebra paridade.
6. **R-19 é o risco que mais muda a ordem.** A pilha-alvo foi decidida (`pilha-alvo-public-v7.md`);
   os contratos de TTL, `route()` e `Str::is` precisam de testes próprios na pilha nova; ver
   `ordem-de-construcao.md` E1.
7. **R-20/R-21 afetam migração, não UI.** O usuário não vê tabela órfã nem catálogo
   duplicado; o custo aparece em relatórios, deduplicação e joins errados.
8. **R-22 é uma decisão de produto.** O legado serve assets versionados sem pipeline;
   adotar Vite é legítimo, mas exige manifesto e revisão de CSP.
9. **R-23 mostra que documento não é oráculo.** O código e o doc divergem em pelo menos
   cinco pontos; a regra é testar o comportamento semanticamente correto e registrar a
   divergência, não congelar o bug.

## Integrabilidade

### Receitas de uso

**1. Adicionar um risco novo.**

```text
ID R-NN; severidade; evidência (doc:linha ou índice); efeito observável;
regra de reconstrução; teste mínimo; decisão reproduzir/corrigir.
```

**2. Bloquear uma entrega por risco.**

Um risco crítico (R-01, R-15, R-16) impede o gate da etapa que o introduz. Riscos altos
(R-02, R-03, R-18, R-19) exigem decisão registrada antes de E1/E2.

**3. Verificar se um risco foi corrigido.**

```bash
# exemplos de testes negativos que devem passar na reconstrução
# GET /cms/v2/gallery sem sessão => 401
# POST /cms/v2/gallery/{id}/rotate sem _token => 419
# bloco SLE com <?php => não executa
# metadata no banco => JSON de objeto, não string
```

## Anti-uso

1. **“Corrigir” R-01 mudando a URL.** O contrato são as 7 rotas; a correção é middleware,
   não novo endpoint.
2. **Sanitizar só na leitura.** R-16 exige sanitização no save **e** no render; conteúdo
   antigo já está no banco.
3. **Trocar `eval` por `include` de arquivo.** Mesmo risco de execução; a substituição
   correta é widget/AST.
4. **Usar `APP_ENV=production` como única defesa.** R-02 mostra que o ambiente real usa
   `prod`; a defesa primária é feature flag + token.
5. **Migrar `backup_*` “por segurança”.** R-20: são restos de rollback, não dados de
   negócio; migrá-los recria o problema.
6. **Tratar os números dos docs como verdade.** R-23: 404 que é 500, 21 que são 36,
   favicon 500 que é 302. O teste segue o contrato corrigido.

## Lacunas

| ID | Lacuna | Impacto |
|---|---|---|
| `LG-REIMPL-11` | ~~Não há decisão registrada sobre a pilha-alvo (R-19)~~ | resolvida em `pilha-alvo-public-v7.md` §1–4 |
| `LG-REIMPL-12` | ~~Não há política de sanitização definida para HTML de BasicPage/SLE~~ | resolvida: conteúdo em AST/jsonb, sem HTML livre; render escapa e sanitiza (`pilha-alvo-public-v7.md` §4–6; ADR-028) |
| `LG-REIMPL-13` | Não há inventário de contas de suporte/IA em produção | R-12 não pode ser dosado |
| `LG-REIMPL-14` | Não há confirmação de quais proxies/CDNs existem na borda | R-03 depende da topologia real |
| `LG-REIMPL-15` | `08_matriz/permissoes-e-papeis.md` ainda não publicado | a matriz de decisão de RBAC usa `_indices/permissoes.csv` |

## Evidências

- Auditoria de segurança: `_qa/security-g1.md:20-214`.
- Revisões de onda: `_qa/review-g2.md:76-153`; `_qa/review-g3.md:49-189`.
- Auth/RBAC: `01_arquitetura/03-auth-rbac-sentinel.md:493,767-770`;
  `01_arquitetura/04-middlewares-seguranca.md:484-507`.
- Runtime/cache: `01_arquitetura/09-cache-csp-minify-maintenance.md:372-374`.
- Build/versões: `01_arquitetura/11-satelites-build-config.md:41,57,226,268,344-345,519,522,553`.
- SLE: `05_modulos/Support/StructuralLandingEditor/spec.md:32,315,926,999-1000`.
- BasicPage: `05_modulos/Layouts/BasicPage/spec.md:88,242,387,409,765`.
- Schema: `_indices/tabelas.csv:12-13,16,19-20,37,39`;
  `01_arquitetura/05-persistencia-uuid-migracoes.md:184,289`;
  `01_arquitetura/12-migracoes-inventario.md:390-407`.
- Tokens regionais: `05_modulos/Conexoes/UnidadeRegional/spec.md:61,761-763`.
