# Falhas de Autorização no CMS — Impacto, Correção e Impacto na v7

> **Continuação de** `16-AUTORIZACAO-ROTA-SEM-AUTENTICACAO.md`.
> La causa raiz, as vulnerabilidades confirmadas em runtime e o inventário dos
> 80 controllers estão no arquivo principal.

---


## 5. Impacto

| Capacidade | Viabilidade |
|---|---|
| Criar/alterar/excluir Fornecedores | **Confirmada** (v5 e v6) |
| Excluir em massa dados da Ouvidoria (assuntos, setores, respostas prontas, formas, origens, tipos) | **Confirmada** |
| Excluir em massa páginas estáticas, enquetes, agenda | Altamente provável (alcançam o controller) |
| Manipular o `fornecedor_ativo` (fornecedor exibido no portal) | **Confirmada** |
| Excluir conteúdo do portal (`contents`), unenquetes, contratos, licitações | Possível — depende da verificação de `check.logged` por rota |
| Criação de conta / escalate de privilégio | A avaliar (`RoleController@store` não checa permissão) |

Como o CMS administra **o conteúdo publicado no portal** (notícias, secretarias,
legislação, diário oficial, transparência), as rotas não protegidas de
`ContentController` e `ArchiveController` Allow-indexação indevida de
informação — um vetor direto de reprovação em auditoria de transparência.

---

## 6. Correção recomendada

### 6.1 Imediato (bloqueia a exploits)

**Opção A — registrar o middleware explicitamente** (mínimo, retrocompatível):

```php
// v6/app/Http/Controllers/Prefecture/SupplierController.php
public function __construct()
{
    parent::__construct();                 // <-- adiciona check.route.permission
    $this->supplier = app(SupplierRepository::class);
}
```

Aplicar nos **80 controllers** listados.

**Opção B — middleware global** (preferível; cobre controllers futuros):

```php
// v6/app/Http/Kernel.php
protected $routeMiddleware = [
    ...
    'check.route.permission' => \App\Http\Middleware\CheckRoutePermission::class,
];

// Registrar como middleware de grupo, em vez de depender do construtor:
protected $middlewareGroups = [
    'cms' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \App\Http\Middleware\CheckRoutePermission::class,   // <-- aqui
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];
```

E trocar o grupo `Route::group(['prefix' => 'cms', 'before' => 'csrf'], ...)`
por `Route::group(['prefix' => 'cms', 'middleware' => ['web', 'cms']], ...)`.

> **Nota sobre `'before' => 'csrf'`:** a opção `'before'` do `Route::group` do
> Laravel 5.5 **não** é a forma correta de aplicar middleware; o correto é
> `'middleware' => [...]`. O CSRF funciona hoje porque o grupo já herda `web`
> (que inclui `VerifyCsrfToken`). O uso de `'before'` é enganoso e frágil.

**Opção C — travar no middleware** (defesa em profundidade):

```php
// CheckRoutePermission::handle()
if (!Sentinel::check()) {
    return redirect()->route('login');   // já existe — mas só roda se registrado
}
```

Manter, e garantir que **todo** controller do CMS registre o middleware.

### 6.2 Corrigir o bug do `array_where`

```php
// ANTES (libera tudo)
$open = array_where($accessArr, function ($value, $key) use (...) {
    if (in_array($value, $openRoutes) || in_array($value, $resourceRoutes)) { return true; }
});

// DEPOIS — comparar a CHAVE (o segmento da rota), não o valor
$open = array_where($accessArr, function ($value, $key) use ($openRoutes) {
    if (in_array($key, $openRoutes) || in_array($key, ['edit','create','update','store','delete','destroy'])) {
        return true;
    }
});
```

> Com a correção, `supplier.update` passa a exigir `supplier.update` de fato
> — e `supplier.index` (segmento `index`) deixa de ser liberado por acidente.

### 6.3 Guarda interna nos controllers sensíveis

Para operações destrutivas, adicionar verificação explícita, independentemente
do middleware:

```php
public function deleteAll(Request $request)
{
    if (!has_access(['ombudsman-subject.delete'])) {
        abort(403);
    }
    ...
}
```

### 6.4 Regras de hygiene

- **Lint/estático:** proibir `__construct()` em controller sem
  `parent::__construct()` — ou exigir `final` no construtor base.
- **Teste de segurança:** para cada rota Write do CMS, um teste que faz a
  requisição **sem cookie de sessão** e **asserta 401/403/302->login**.
  Este teste pega automaticamente toda regressão futura.

---

## 7. Impacto na reconstrução (v7)

- **Autorização por padrão, não por omissão.** Na v7, o middleware de
  autorização deve ser aplicado no **grupo de rotas** do CMS, nunca no
  construtor de cada controller.
- **Policies do Laravel** (`Gate::define` / `can:`) no lugar do
  `has_access()` e do `CheckRoutePermission`.
- **Middleware `auth` + `can:`** explícito por rota, com testes.
- **Teste automatizado de superfície**: enumerar todas as rotas de escrita e
  exigir 403 sem autenticação. Esse é o teste que teria pegado isso.

---

## 8. Reprodução

```bash
HOST=https://localhost:35979          # v6
# HOST=https://localhost:42280        # v5

# 1) obter sessão anônima + token CSRF
curl -sk -c jar.txt "$HOST/login" -o page.html
TOKEN=$(grep -oP 'name="csrf-token" content="\K[^"]+' page.html)

# 2) criar fornecedor sem autenticação (v6)
curl -sk -b jar.txt -c jar.txt -X POST "$HOST/cms/fornecedores" \
     -H "X-CSRF-TOKEN: $TOKEN" \
     --data 'name=TesteAnon&email=a@b.com&site=https://x.com&phone=11999999999&address=Rua 1&locale=1'

# 3) excluir assunto da ouvidoria sem autenticação
curl -sk -b jar.txt -c jar.txt -X POST "$HOST/cms/ajax/assuntos/deleteAll" \
     -H "X-CSRF-TOKEN: $TOKEN" --data 'toDelete[]=1'
```

Resultado esperado (não corrigido): **HTTP 302/200 com sucesso** e a linha
criada/excluída na base.

---

## 9. Referências

| Item | Caminho |
|---|---|
| Registro do middleware (origem) | `v6/app/Http/Controllers/Prefecture/Controller.php:20-24` |
| Controller sem `parent::__construct()` | `v6/app/Http/Controllers/Prefecture/SupplierController.php:30-33` |
| Rotas de escrita sem `check.logged` | `v6/routes/basepro.php:349-355` |
| `deleteAll` sem guarda | `v6/app/Http/Controllers/Prefecture/OmbudsmanSubjectController.php:91-108` |
| `deleteAll` de setores (confirmado) | `v6/app/Http/Controllers/Prefecture/OmbudsmanSectorController.php:97-115` |
| Bug do `array_where` | `v6/app/Http/Middleware/CheckRoutePermission.php:44-93` |
| Guard interna ausente | `v6/app/Http/Controllers/Prefecture/SupplierController.php:62-140` |
| Equivalente v5 | `v5/app/Http/Controllers/Prefecture/SupplierController.php:30-33` |
| Validação mais estrita na v5 | `v5/app/Http/Requests/SupplierRequest.php:26-37` |
