# Feature Specification: Cibersegurança do Wizard — Telemetria em Tempo Real (021)

**Criado:** 2026-06-17  
**Solicitado por:** Roger  
**Status:** Spec — implementação pendente  
**Prioridade:** P1  
**Sistema:** Wizard VM112 (`/opt/ligbox-wizard`) + Portal onboard + Desk VM122  
**Módulo Desk:** `wizard-security` (novo — Spec 015)  
**Depende de:** Spec 001/014 (webhooks + funil), Spec 015 (módulos), Spec 016 (handoff + sessionStorage)  
**UI principal:** **Audit Overview** → tenant VM112 + **Infra 2** + **Eventos**

---

## Resumo

Estender a observabilidade do onboarding com **vias de cibersegurança** dos processos do wizard: CSP (browser), auditoria de inputs (API VM112), integridade do handoff server-side e eventos de abuso — tudo visível **em tempo quase real** no Desk VM122, na mesma sessão (`session_id` / hash) do funil.

**Princípio:** isto é **camada extra de detecção e resposta** — complementa (não substitui) HTTPS, handoff one-time, React escaping e ausência de SQL no fluxo de credenciais.

**Regra de ouro:** **nunca** enviar senhas, tokens de handoff completos nem corpos de request com PII sensível nos webhooks de segurança.

---

## Problema

| Hoje | Necessidade |
|------|-------------|
| Desk vê **progresso** do funil (`session.started` → `completed`) | Ver também **tentativas de abuso** na mesma sessão |
| Audit Overview = saúde infra (Carbonio, DNS, cert) | Card **Segurança onboarding** por tenant VM112 |
| CSP / validação de inputs inexistentes ou invisíveis | Política activa + relatórios no Desk |
| IA/fuzzing acelera ataques | Alertas operacionais sem esperar abuse report |

---

## Modelo de ameaças (wizard VM112)

| Ameaça | Camada actual | Gap | Via Spec 021 |
|--------|---------------|-----|--------------|
| **XSS** (ler sessionStorage) | React escape + sem `dangerouslySetInnerHTML` | Sem CSP nem reporte | CSP + `security.csp_violation` |
| **SQL injection** | Handoff sem SQL (JSON encriptado) | Outras APIs futuras | Middleware `security.input_blocked` |
| **Roubo handoff token** | Token opaco, TTL 15 min, one-shot | Sem telemetria de reutilização | `security.handoff_rejected` |
| **Senha na URL** | Nunca — só `?onboard_handoff=` | — | Auditoria confirma ausência |
| **MITM** | HTTPS Let's Encrypt | — | Fora de escopo (já coberto) |
| **Fuzzing / brute** | Parcial | Sem rate limit visível | `security.rate_limited` |
| **Path traversal / SSRF** | Não auditado | — | `security.input_blocked` |
| **IA a gerar payloads** | Mesmas defesas | Sem feed SOC | Eventos no Desk em segundos |

### O que sessionStorage **não** é

- **Não** protege contra SQL injection (corre só no browser).
- **Risco real:** XSS bem-sucedido → script lê `ligbox_onboard_password`.
- **Resposta Spec 021:** CSP reduz superfície + reporta violações; inputs maliciosos bloqueados antes de chegar ao estado.

---

## Vias de processo — mapa completo

### Via 1 — Credenciais (Portal → Wizard)

```
Utilizador (onboard.ligbox.com.br)
  → Self-Service / Login (HTTPS)
  → POST /onboard-handoff  [VM112 API — senha encriptada server-side]
  → redirect ?onboard_handoff=<token_opaco>
  → POST /consume  [one-shot, apaga token]
  → sessionStorage.ligbox_onboard_password  [temporário, mesma origem]
  → wizard /onboard
```

**Eventos de segurança:**

| Evento | Quando |
|--------|--------|
| `security.handoff_created` | Handoff emitido (sem senha no payload) |
| `security.handoff_consumed` | Consume OK |
| `security.handoff_rejected` | Token expirado, reutilizado, session mismatch |
| `security.handoff_expired` | TTL 15 min excedido |

### Via 2 — Inputs do wizard (passos 0–N)

Rotas VM112 a auditar (mínimo):

| Endpoint / acção | Campos |
|------------------|--------|
| Validação domínio | `domain`, FQDN |
| Conta admin | `localPart`, `domain`, `email` |
| DNS / Cloudflare | `domain`, records |
| Portal users | `login_id`, `planned_corporate_email` |
| Company profile | `legal_name`, `tax_id`, texto livre |
| Handoff consume | `token`, `session_id` |

**Eventos:**

| Evento | Severidade | Acção API |
|--------|------------|-----------|
| `security.input_warn` | baixa | Sanitizar + log + webhook |
| `security.input_blocked` | alta | HTTP 400 + log + webhook |
| `security.rate_limited` | média | HTTP 429 + webhook |

**Padrões detectados (regex/heurística MVP):**

- SQLi: `' OR `, `UNION SELECT`, `; DROP`, `1=1--`
- XSS: `<script`, `javascript:`, `onerror=`, `onload=`
- Path: `../`, `%2e%2e`
- Command: `` ` ``, `$(`, `|`, `&&`
- Oversize: campo > limite (ex. domínio > 253, nome > 500)

**Nunca logar:** `password`, `root_password`, corpo de `/consume` com segredos.

### Via 3 — CSP (browser → Desk)

```
Browser (portal + wizard)
  → viola Content-Security-Policy
  → POST report-uri / report-to
  → VM122 /api/v1/security/csp-report
  → webhook_events (source: vm112-security)
  → Audit Overview + Infra 2 + Eventos
```

**Header CSP (Traefik / nginx — portal + wizard):**

```
Content-Security-Policy:
  default-src 'self';
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://desk.ligbox.com.br;
  frame-ancestors 'none';
  base-uri 'self';
  report-uri https://desk.ligbox.com.br/api/v1/security/csp-report;
```

(Ajustar `connect-src` para APIs VM112/Traefik em produção.)

**Evento:** `security.csp_violation` — inclui `blocked-uri`, `violated-directive`, `document-uri` (sem dados de utilizador).

### Via 4 — Correlação com funil (VM122)

Cada evento `security.*` **deve** incluir quando disponível:

- `session_id` (hash UUID)
- `domain`
- `client_ip` (ingress)
- `endpoint` / `wizard_step`
- `severity`: `info` | `warn` | `high` | `critical`

O Desk correlaciona com timeline Spec 014 na mesma sessão.

---

## Módulo Desk (Spec 015)

| Campo | Valor |
|-------|--------|
| `id` | `wizard-security` |
| `label` | Segurança Wizard |
| `default_enabled` | `true` |
| `nav_views` | _(enrichment — Audit Overview, Infra 2, Eventos)_ |

Desactivar módulo → APIs devolvem payload sem `security_summary`; UI oculta card e filtro.

---

## RBAC

| Acção | Perfis |
|-------|--------|
| Ver card Segurança no Audit Overview | `super_admin`, `ops_lead` |
| Ver feed segurança Infra 2 | `super_admin`, `ops_lead`, `noc` |
| Ver eventos `security.*` em Eventos | `super_admin`, `ops_lead`, `noc` |
| POST `csp-report` | Público (browser) — rate limit + validação schema |
| POST webhook segurança VM112 | `X-Webhook-Secret` (mesmo ou derivado de onboard) |

Técnicos `technician` — sem card segurança (opcional: só tickets ligados).

---

## API Desk (VM122)

### Ingestão

| Método | Path | Auth | Descrição |
|--------|------|------|-----------|
| POST | `/api/v1/security/csp-report` | nenhum (browser) | Relatório CSP (JSON W3C ou legacy) |
| POST | `/api/v1/webhooks/security` | `X-Webhook-Secret` | Eventos VM112 `security.*` |
| POST | `/api/v1/webhooks/onboard` | existente | Aceitar também `security.*` no mesmo ingress (opcional) |

### Consulta

| Método | Path | Descrição |
|--------|------|-----------|
| GET | `/api/v1/security/events?window=24h&session_id=` | Lista eventos segurança |
| GET | `/api/v1/security/summary?tenant_id=1` | KPIs para Audit Overview |
| GET | `/api/v1/audit/tenants/1/details` | Enriquecido com `security_summary` + `security_events_recent` |

### Payload webhook (VM112 → VM122)

```json
{
  "event": "security.input_blocked",
  "session_id": "ee2239fd-dd05-444e-b79c-a5701a255ba8",
  "domain": "evil.example",
  "data": {
    "endpoint": "POST /api/domains/validate",
    "field": "domain",
    "reason": "sql_injection_pattern",
    "pattern_id": "sqli_union",
    "client_ip": "203.0.113.42",
    "wizard_step": 0,
    "severity": "high"
  }
}
```

**Proibido no payload:** passwords, tokens handoff completos, headers `Authorization`.

### Persistência

Tabela nova ou reutilizar `webhook_events`:

| Opção | Prós |
|-------|------|
| **A** — `webhook_events` com `source=vm112-security` | Reutiliza Eventos, Infra 2, funil |
| **B** — tabela `security_events` dedicada | Queries mais rápidas, retenção própria |

**MVP:** opção A (consistente com arquitectura actual).

Índices recomendados: `(source, created_at)`, `(session_id)`, `(event_type)`.

---

## API VM112 (wizard)

### Middleware `security_audit.py` (novo)

- Executar **antes** do handler em rotas listadas na Via 2.
- Retornar 400/429 com corpo genérico (não revelar qual regex matched em produção — opcional `reason` interno no webhook only).
- Fire-and-forget POST para VM122 (não bloquear UX se Desk offline).

### Cliente webhook

Reutilizar cliente existente de onboarding (`session.started`, etc.) com fila retry (3 tentativas, backoff 2s).

### Config (.env VM112)

```
DESK_SECURITY_WEBHOOK_URL=https://desk.ligbox.com.br/api/v1/webhooks/security
DESK_WEBHOOK_SECRET=<mesmo WEBHOOK_SECRET>
SECURITY_AUDIT_ENABLED=true
SECURITY_RATE_LIMIT_PER_IP=60/min
```

---

## UI Desk

### Audit Overview → modal VM112 Ligbox Onboard

Novo bloco **«Segurança onboarding»** (acima ou abaixo do resumo domínios):

| KPI | Exemplo |
|-----|---------|
| Violações CSP (24h) | 3 |
| Inputs bloqueados | 1 |
| Handoffs rejeitados | 0 |
| Sessões com alerta | 2 |

Lista recente (clicável → detalhe):

| Hora | Sessão (hash) | Evento | IP | Domínio |
|------|---------------|--------|-----|---------|
| 21:42 | `ee2239fd…` | `security.csp_violation` | 203.0.113.1 | — |
| 21:40 | `3dfa8c6c…` | `security.input_blocked` | 198.51.100.5 | `foo';DROP--` |

Clique → modal com timeline **funil + segurança** intercalados (ou abas).

### Infra 2 (SOC)

- Painel **«Segurança wizard»** no feed (15s refresh).
- Flash visual em `security.input_blocked` e `security.csp_violation` (como eventos novos no feed VM112).

### Eventos

- Filtro toolbar: **Segurança** (`source=vm112-security` ou `event` prefix `security.`).
- Colunas: severidade, evento, sessão (hash completo), domínio, IP, hora.

### Tickets (opcional Fase C)

Auto-ticket quando:

- ≥3 `security.input_blocked` mesmo IP em 10 min, ou
- `security.csp_violation` + `security.input_blocked` mesma sessão

Subject: `[security] {domain|sem domínio} — {event}` · prioridade alta.

---

## Taxonomia de eventos `security.*`

| event | Label UI | Severidade default |
|-------|----------|-------------------|
| `security.csp_violation` | Violação CSP | warn |
| `security.input_warn` | Input suspeito (sanitizado) | info |
| `security.input_blocked` | Input bloqueado | high |
| `security.rate_limited` | Rate limit | warn |
| `security.handoff_created` | Handoff criado | info |
| `security.handoff_consumed` | Handoff consumido | info |
| `security.handoff_rejected` | Handoff rejeitado | high |
| `security.handoff_expired` | Handoff expirado | info |
| `security.auth_failed` | Autenticação portal falhou (agregado) | warn |
| `security.session_anomaly` | Sessão inconsistente (IDs mismatch) | high |

---

## Fases de implementação

### Fase A — Ingestão VM122 (Desk)

- [ ] T001 `POST /api/v1/security/csp-report` + validação schema + rate limit
- [ ] T002 `POST /api/v1/webhooks/security` (ou extensão onboard ingress)
- [ ] T003 Persistência `webhook_events` source `vm112-security`
- [ ] T004 `GET /api/v1/security/summary` e `/security/events`
- [ ] T005 Registar módulo `wizard-security` em `registry.py`

### Fase B — VM112 wizard

- [ ] T006 Middleware auditoria inputs (Via 2)
- [ ] T007 Eventos handoff (Via 1)
- [ ] T008 Cliente webhook segurança + retry
- [ ] T009 Testes unitários padrões SQLi/XSS

### Fase C — UI Desk

- [ ] T010 Card Segurança no Audit Overview modal VM112
- [ ] T011 Filtro Eventos «Segurança»
- [ ] T012 Painel Infra 2 + flash eventos novos
- [ ] T013 Hash sessão na lista (já feito — correlacionar)

### Fase D — Infra Traefik/nginx

- [ ] T014 CSP headers portal + wizard (CT114 Traefik)
- [ ] T015 Validar `report-uri` reachability desde browser público
- [ ] T016 Documentar excepções CSP se libs externas exigirem

### Fase E — Resposta operacional (opcional)

- [ ] T017 Auto-ticket regras abuso
- [ ] T018 Push ntfy em `security.input_blocked` critical
- [ ] T019 Retenção 90 dias + purge `security.*` antigos

---

## Critérios de aceitação

1. Browser com CSP activo envia violação → evento visível em Eventos em <30s.
2. POST domínio `foo<script>alert(1)</script>.com` → 400 VM112 + `security.input_blocked` no Desk.
3. Reutilizar token handoff → `security.handoff_rejected` correlacionado à `session_id`.
4. Audit Overview VM112 mostra KPIs segurança 24h sem regressão nos domínios existentes.
5. Módulo `wizard-security` OFF → card oculto; APIs sem `security_summary`.
6. Nenhum webhook contém senha nem token handoff completo (auditoria manual + teste).
7. Mesma sessão: funil (`session.started`) + evento segurança partilham `session_id` no detalhe.

---

## Testes

```bash
# CSP report (simulado)
curl -s -X POST https://desk.ligbox.com.br/api/v1/security/csp-report \
  -H 'Content-Type: application/csp-report' \
  -d '{"csp-report":{"document-uri":"https://onboard.ligbox.com.br/onboard","violated-directive":"script-src","blocked-uri":"https://evil.com/x.js"}}'

# Input blocked (após Fase B)
curl -s -X POST https://onboard.ligbox.com.br/api/.../validate \
  -d '{"domain":"x\"; DROP TABLE--"}' 
# → 400 + evento no Desk

# Summary Desk
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://desk.ligbox.com.br/api/v1/security/summary?tenant_id=1"
```

---

## Fora de escopo (v1)

- WAF comercial (Cloudflare WAF rules — futuro)
- SIEM externo (export syslog)
- Pentest automatizado no wizard
- Substituir sessionStorage por Web Crypto / Credential Management API
- Segurança Carbonio pós-provisionamento (Spec separada)

---

## Relação com specs existentes

| Spec | Relação |
|------|---------|
| **016** | Handoff + sessionStorage — Via 1 auditada, senha nunca no webhook |
| **014** | Relógio por fase — mesma sessão, timeline paralela segurança |
| **015** | Módulo `wizard-security` |
| **017** | Purge — independente; não apagar `security.*` antes de retenção |
| **010** | Assist/takeover — técnico vê alertas segurança na sessão assistida |

---

## Conclusão

A Spec 021 define as **vias de cibersegurança** dos processos do wizard (credenciais, inputs, CSP, handoff) com **telemetria em tempo real no VM122** — Audit Overview, Infra 2 e Eventos — sem confundir protecção (HTTPS, handoff, React) com **visibilidade operacional** (o que o Roger precisa para operar e reagir).

**Próximo passo:** Fase A (ingestão VM122) — pode começar sem alterações no wizard; Fase B liga a detecção activa na VM112.
