# F5.I4.T3 — Sem reescrita de `<img>`; upload seguro (MIME real, tamanho, checksum)

| Campo | Valor |
|---|---|
| **Fase** | F5 — Borda |
| **Issue** | F5.I4 — Media e Gallery (plano 19) |
| **kd task** | `task_00zpq34r` |
| **Âncora** | `wiki/implementation/05-borda.md` |
| **Status da revisão** | ✅ revisado |

## O que foi implementado

- Gate estrutural `MediaNoRewriteTest`: nenhum middleware/`Internal/Http` constrói `<img>` nem usa
  `DOMDocument`; `bootstrap/app.php` sem rewriter.
- MIME real (finfo + allowlist) na **borda**, limite por tipo, `checksum` SHA-256 e quarentena
  `staging/` **404** no stream público (`MediaStreamController`: prefixo `permanent/`, sem `..`/byte
  nulo, extensão conhecida, `CSP sandbox` para SVG).

## Arquivos para revisar

- `tests/Feature/MediaNoRewriteTest.php`
- `src/services/Media/Http/MediaStreamController.php`
- `src/services/Media/Storage/SecureMediaStorage.php`
- `src/services/Media/Http/Requests/MediaUploadRequest.php` · `Rules/WithinMediaLimit.php`

## Pontos de revisão

### PHP 8.4 / Laravel 12
- [ ] `declare(strict_types=1)`; DI por construtor (sem `new`/`app()`/`resolve()` no corpo); PHPStan `max`.
- [ ] Nativo primeiro; sem closure em rota; `env()` só em `config/`; validação em `FormRequest`.

### Determinismo (ADR-001)
- [ ] `Clock`/`IdGen` injetados; ordem explícita; idempotência provada por teste.

### Fronteiras (ADR-013/014/015)
- [ ] Camadas respeitadas; módulo consome `Services\{Name}\{Contracts,DTOs,Enums}\*`; nada de storage fora do Media.

### HTML/a11y (ADR-025) e CSS (ADR-010)
- [ ] Semântica, rótulos, alvos; sem `!important`/inline/CDN; `oklch`/`rem`.

### i18n (ADR-012)
- [ ] Nenhuma string visível fixa — inclusive **títulos de erro lançados em PHP** (o `ModuleI18nTest` só varre views).

### Dados (ADR-007/017) e segurança (ADR-020/021/026/030/035)
- [ ] `text`/`jsonb`/`uuidv7()`; `down()` → `backup_*`; CHECKs; PII cifrada; `throttle`; MIME real; `alt` obrigatório.

### Observabilidade/auditoria (ADR-024/035)
- [ ] Span + `log_event` com o **mesmo** `{service}.{action}`; auditoria via `AuditWriter`; sem PII.

### Específicos desta task

- [ ] Nenhum rewriter de resposta; a mídia é decidida no componente, não no middleware.
- [ ] Stream público só `permanent/`; quarentena inalcançável; SVG inerte (`sandbox`).
- [ ] MIME real, tamanho e `checksum` verificados no servidor.

## Comandos úteis

```bash
make check              # caminho único (rápido)
make check E2E=1        # + Playwright/axe/perf (2× ao fechar o escopo)
```

---

> Revisar contra `AGENTS.md`, `wiki/adr/` e o plano citado. Registrar achados no `kd` (`kd write --anchor`) e, se houver correção, teste de regressão.

## Notas da revisão

| Data | Revisor | Resultado | Observações |
|---|---|---|---|
| 2026-10-19 | agente (PROTOCOLO v2.0) | ✅ | Sem violação de invariante/ADR/segurança. **Verificado OK**: `MediaNoRewriteTest`
(middleware e `bootstrap/app.php`), `normalizePath()` recusa travessia/byte nulo/prefixo estranho,
`CSP: default-src 'none'; sandbox` no SVG, `Cache-Control immutable` no objeto imutável, quarto
`ok` no teste de storage. Observação (D3/D7, baixo): o SVG servido é o original do autor — a
`sandbox` da resposta é a barreira, não há sanitização do XML (aceitável com a CSP estrita +
`XML` sem `script` do editor; vale revisitar se upload de SVG passar a aceitar link externo). |

> Resultado: `✅ revisado` · `🔧 com correções` · `⏭️ delegado` (com justificativa).
