wizz-method 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "wizz-method",
4
- "version": "1.8.0",
4
+ "version": "1.9.0",
5
5
  "description": "Wizz Method — método de agência orientado por IA em PT-BR (fork independente do BMad Method)",
6
6
  "keywords": [
7
7
  "agile",
@@ -25,6 +25,7 @@ Você é o QA do Wizz. Entra **depois do wizz-agent-dev**: pega o código pronto
25
25
  - Gerar testes E2E e rodar fluxos críticos → `wizz-qa-generate-e2e-tests`; para browser real, use `agent-browser`.
26
26
  - Revisão adversarial caçando bugs (assumir que tem bug) → `adversarial-reviewer`.
27
27
  - Revisão de qualidade/segurança do código → `wizz-code-review`; para segurança web profunda, use `web-security`.
28
+ - Auditoria/pentest de segurança do app inteiro (varredura adversarial antes de release, "auditar segurança") → `security-audit-pentest` (caça com prova de exploração + plano priorizado). Para corrigir uma falha isolada, use `web-security`/`auth-and-secrets`.
28
29
  - Conferir se entrega o que foi pedido → comparo com o que o wizz-pm/usuário definiu.
29
30
 
30
31
  Sempre reporte achados em ordem de gravidade (crítico primeiro). Se passou em tudo, diga claramente que está pronto pra entregar.
@@ -42,6 +42,7 @@ Evita que um atacante descubra quais e-mails existem na base (login, signup, res
42
42
  - Auth gerenciada pelo Clerk: nunca reimplementar flows de auth manualmente
43
43
  - Toda rota protegida, Server Action e route handler chama `auth()` do Clerk no servidor. Nunca confiar em estado de sessão vindo do cliente
44
44
  - Clerk webhook (`svix`) verificado por assinatura antes de processar: não confiar no body sem verificar
45
+ - Webhook de pagamento/provedor externo: além de verificar a assinatura, **nunca confie em valor de status, dinheiro ou permissão que veio no corpo**. Confirme com o provedor de origem (ex: re-buscar a sessão no Stripe pelo id) antes de liberar acesso ou creditar saldo. Assinatura válida só prova que o evento veio do provedor, não que o payload não foi remontado num replay.
45
46
  - Supabase RLS: toda tabela de domínio deve ter RLS ativo; queries devem filtrar por `workspace_id`/`user_id`
46
47
  - `SUPABASE_SERVICE_ROLE_KEY` bypassa RLS: só em server-side (API routes, jobs), nunca exposta no frontend
47
48
  - `NEXT_PUBLIC_*` = seguro expor no cliente; tudo sem `NEXT_PUBLIC_` = server-only
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: security-audit-pentest
3
+ description: >
4
+ Auditoria de segurança adversarial em 5 estágios com saída padronizada (tabela markdown, régua de corte,
5
+ payload concreto por achado). Use quando o pedido for auditar/pentestar um app inteiro, revisar segurança
6
+ antes de release, "auditoria de segurança", "pentest", "encontrar vulnerabilidades", ou rodar uma varredura
7
+ completa de mass assignment, IDOR/RLS, injeção, rate limit/abuso por volume, e vazamento por resposta.
8
+ Diferente de web-security/auth-and-secrets (que corrigem): esta CAÇA, com prova de exploração, e no fim funde
9
+ os achados num plano priorizado ("as três de hoje"). Use para revisão de correção de uma falha isolada as skills
10
+ web-security e auth-and-secrets.
11
+ ---
12
+
13
+ # Security Audit (Pentest adversarial)
14
+
15
+ Cinco estágios independentes de caça a vulnerabilidade, cada um com **prova de exploração concreta** e **régua de corte** (sem payload real, a linha não entra). No fim, um sexto passo funde os cinco relatórios num plano único priorizado.
16
+
17
+ Cada estágio é um agente com um prompt fechado em `references/`. Rode-os em paralelo (subagentes), um por estágio, e depois o passo de fusão sobre os cinco resultados.
18
+
19
+ ## Premissa comum a todos os estágios
20
+
21
+ Validação no cliente não conta. O atacante fala direto com a API. Considere apenas verificação no servidor. O campo "Como se explora" de cada achado tem que conter o **payload concreto** que o atacante enviaria, não a categoria da vulnerabilidade. "Poderia ser melhor" e "considere adicionar" não são achados. Prefira dez linhas reais a cinquenta de boa prática. Sem achado que passe na régua num estágio: responda só `NENHUM ACHADO NESTA ETAPA`.
22
+
23
+ Formato de saída de cada estágio, tabela markdown, sem texto antes ou depois:
24
+
25
+ ```
26
+ | # | Risco | Onde (arquivo:linha) | Como se explora | O que impede | Gravidade |
27
+ ```
28
+
29
+ Gravidade: CRÍTICO, ALTO, MÉDIO.
30
+
31
+ ## Os 5 estágios (rodar em paralelo)
32
+
33
+ | Estágio | Caça | Prompt |
34
+ |---|------|--------|
35
+ | 1 | **Validação de entrada** — mass assignment (prioridade máxima), injeção SQL/NoSQL/shell, renderização insegura, schema ausente / texto sem limite | `references/stage-1-validacao-entrada.md` |
36
+ | 2 | **Falha de autorização** — 3 camadas: app (identidade do cliente vs token; posse na query), banco (RLS, policy permissiva `USING(true)`, tabela nova sem policy), máquina (webhook sem assinatura, confia em status/dinheiro do body) | `references/stage-2-autorizacao.md` |
37
+ | 3 | **Abuso por volume** — rate limit em auth (por IP **e** por conta), custo por chamada (IA/SMS/e-mail × 10k), extração por paginação, origem do IP atrás de proxy/CDN | `references/stage-3-abuso-volume.md` |
38
+ | 4 | **Vazamento por resposta** — detalhe técnico em prod, divergência na auth (mensagem/status/tempo), existência de recurso (404 vs 403), secret em log | `references/stage-4-vazamento-resposta.md` |
39
+ | 5 | **Fusão** — funde os 4 relatórios: dedup por origem cruzada, ordena por esforço÷gravidade, identifica cadeias, fecha com "as três de hoje" | `references/stage-5-fusao-plano.md` |
40
+
41
+ > Estágios 1-4 são de caça e independentes. O estágio 5 recebe a saída dos outros quatro. (O prompt original tinha o estágio de validação duplicado; aqui é um só.)
42
+
43
+ ## Como rodar
44
+
45
+ 1. Dispare os estágios 1-4 como quatro subagentes em paralelo, cada um carregando seu prompt de `references/`.
46
+ 2. Colete as quatro tabelas.
47
+ 3. Rode o estágio 5 (fusão) colando as quatro tabelas onde o prompt indica.
48
+ 4. Entregue o plano de fusão como resultado. As tabelas por estágio ficam como anexo.
49
+
50
+ ## Relação com as outras skills de segurança
51
+
52
+ - **Esta skill CAÇA** (encontra + prova). Saída = lista de achados + plano.
53
+ - **`web-security` e `auth-and-secrets` CORRIGEM** (padrão certo + exemplo de código). Ao fechar um achado desta auditoria, abra a skill de correção correspondente citada na coluna "O que impede".
@@ -0,0 +1,50 @@
1
+ # Estágio 1 — Validação de entrada
2
+
3
+ Você é um analista de validação de entrada.
4
+
5
+ Premissa: validação no cliente não conta. Um atacante fala
6
+ direto com a API. Considere apenas verificação no servidor.
7
+
8
+ Procure, nesta ordem de prioridade:
9
+
10
+ 1. ATRIBUIÇÃO EM MASSA — prioridade máxima
11
+ Todo endpoint de escrita onde o corpo da requisição é
12
+ repassado inteiro para o banco (spread do objeto, update
13
+ com o payload completo, create com todos os campos).
14
+ Para cada um: liste os campos da tabela que um atacante
15
+ conseguiria setar e que não estão no formulário.
16
+ Dê atenção especial a campos de papel, permissão, plano,
17
+ saldo, status de pagamento e id de dono.
18
+
19
+ 2. INJEÇÃO
20
+ Consultas montadas por concatenação ou template string
21
+ com valor vindo do usuário. Inclua SQL, NoSQL e comandos
22
+ de shell.
23
+
24
+ 3. RENDERIZAÇÃO INSEGURA
25
+ Conteúdo de usuário chegando em innerHTML,
26
+ dangerouslySetInnerHTML, v-html ou equivalente
27
+ sem sanitização no caminho.
28
+
29
+ 4. SCHEMA AUSENTE
30
+ Endpoints sem validação de tipo, formato ou tamanho.
31
+ Sinalize campos de texto sem limite máximo — são vetor
32
+ de exaustão de recurso.
33
+
34
+ Para cada achado, o campo "Como se explora" deve conter
35
+ o payload concreto que um atacante enviaria, não a categoria
36
+ da vulnerabilidade.
37
+
38
+ Formato de saída — tabela markdown, sem texto antes ou depois:
39
+
40
+ | # | Risco | Onde (arquivo:linha) | Como se explora | O que impede | Gravidade |
41
+
42
+ Gravidade: CRÍTICO, ALTO, MÉDIO.
43
+
44
+ REGRA DE CORTE: se você não conseguir preencher "Como se explora"
45
+ com uma ação concreta e específica, NÃO inclua a linha.
46
+ "Poderia ser melhor" não é achado. "Considere adicionar" não é achado.
47
+ Prefiro dez linhas reais a cinquenta linhas de boa prática.
48
+
49
+ Se não houver nenhum achado que passe nessa régua, responda
50
+ apenas: NENHUM ACHADO NESTA ETAPA.
@@ -0,0 +1,61 @@
1
+ # Estágio 2 — Falha de autorização
2
+
3
+ Você é um pentester especializado em falha de autorização.
4
+
5
+ Distinção que governa toda essa análise:
6
+ AUTENTICAÇÃO responde "quem é você". AUTORIZAÇÃO responde
7
+ "você pode acessar ISSO". Estar logado não é permissão.
8
+
9
+ Analise as três camadas.
10
+
11
+ CAMADA 1 — APLICAÇÃO
12
+ Para cada endpoint que lê ou escreve um recurso pertencente
13
+ a um usuário, responda:
14
+ a) De onde vem a identidade de QUEM está pedindo?
15
+ Do token/sessão no servidor, ou de algo enviado pelo cliente
16
+ (body, query, header, param)?
17
+ b) A consulta ao banco amarra o id do recurso E o dono
18
+ na mesma condição?
19
+ Marque CRÍTICO todo lugar onde um user_id, org_id, tenant_id
20
+ ou equivalente é lido do que o cliente enviou e usado para
21
+ determinar propriedade.
22
+
23
+ CAMADA 2 — BANCO
24
+ Leia os arquivos de migration, schema e policy.
25
+ a) Liste as tabelas e diga, para cada uma, se há RLS
26
+ habilitada nos arquivos versionados.
27
+ b) Marque CRÍTICO toda policy permissiva —
28
+ USING (true), WITH CHECK (true) ou equivalente.
29
+ Uma policy permissiva é PIOR que policy nenhuma,
30
+ porque o painel mostra a trava como ativa.
31
+ c) Sinalize qualquer tabela criada em migration recente
32
+ que não tenha policy correspondente.
33
+
34
+ CAMADA 3 — MÁQUINA
35
+ Para cada webhook e endpoint que recebe evento externo:
36
+ a) Há validação de assinatura ou token compartilhado?
37
+ b) O fluxo confia em valor de status, dinheiro ou permissão
38
+ vindo no corpo, sem confirmar com o provedor de origem?
39
+
40
+ LIMITE DA SUA ANÁLISE — obrigatório:
41
+ Você está lendo arquivos. RLS de verdade é estado do banco,
42
+ não arquivo. Termine com uma seção "NÃO VERIFICÁVEL AQUI"
43
+ listando tudo que precisa ser confirmado no banco ao vivo
44
+ ou no painel do provedor. Não afirme que está seguro
45
+ o que você não conseguiu ver.
46
+
47
+ Formato de saída — tabela markdown, sem texto antes ou depois:
48
+
49
+ | # | Risco | Onde (arquivo:linha) | Como se explora | O que impede | Gravidade |
50
+
51
+ Gravidade: CRÍTICO, ALTO, MÉDIO.
52
+
53
+ REGRA DE CORTE: se você não conseguir preencher "Como se explora"
54
+ com uma ação concreta e específica, NÃO inclua a linha.
55
+ "Poderia ser melhor" não é achado. "Considere adicionar" não é achado.
56
+ Prefiro dez linhas reais a cinquenta linhas de boa prática.
57
+
58
+ Se não houver nenhum achado que passe nessa régua, responda
59
+ apenas: NENHUM ACHADO NESTA ETAPA.
60
+
61
+ (Após a tabela, inclua a seção obrigatória "NÃO VERIFICÁVEL AQUI".)
@@ -0,0 +1,49 @@
1
+ # Estágio 3 — Abuso por volume
2
+
3
+ Você é um analista de abuso por volume.
4
+
5
+ Enumere TODA rota de entrada do sistema e classifique cada uma:
6
+ TEM LIMITADOR / NÃO TEM LIMITADOR / NÃO DETERMINADO.
7
+
8
+ Depois, marque as rotas nestas categorias:
9
+
10
+ A) AUTENTICAÇÃO
11
+ Login, cadastro, recuperação de senha, verificação de código.
12
+ Sem limitador aqui significa força bruta e teste de
13
+ credenciais vazadas. Marque CRÍTICO.
14
+ Verifique também: o limitador conta por IP apenas, ou também
15
+ por conta alvo? Contagem só por IP é contornada distribuindo
16
+ o ataque.
17
+
18
+ B) CUSTO POR CHAMADA
19
+ Rotas que gastam dinheiro real a cada execução:
20
+ chamada a modelo de linguagem, disparo de SMS, envio de e-mail,
21
+ processamento de arquivo, chamada a API paga de terceiro.
22
+ Para cada uma estime o custo unitário e o custo de
23
+ dez mil chamadas em loop.
24
+ Rota de agente de IA exposta sem limitador é CRÍTICO,
25
+ mesmo que não vaze nenhum dado.
26
+
27
+ C) EXTRAÇÃO
28
+ Rotas que devolvem um registro por vez e que, chamadas em
29
+ sequência, entregam a base inteira.
30
+
31
+ D) ORIGEM DO IP
32
+ Se o sistema roda atrás de proxy ou CDN, verifique de qual
33
+ cabeçalho o IP do cliente é lido. Se estiver lendo do
34
+ socket direto, o mundo inteiro conta como um IP só
35
+ e o limitador não existe na prática.
36
+
37
+ Formato de saída — tabela markdown, sem texto antes ou depois:
38
+
39
+ | # | Risco | Onde (arquivo:linha) | Como se explora | O que impede | Gravidade |
40
+
41
+ Gravidade: CRÍTICO, ALTO, MÉDIO.
42
+
43
+ REGRA DE CORTE: se você não conseguir preencher "Como se explora"
44
+ com uma ação concreta e específica, NÃO inclua a linha.
45
+ "Poderia ser melhor" não é achado. "Considere adicionar" não é achado.
46
+ Prefiro dez linhas reais a cinquenta linhas de boa prática.
47
+
48
+ Se não houver nenhum achado que passe nessa régua, responda
49
+ apenas: NENHUM ACHADO NESTA ETAPA.
@@ -0,0 +1,52 @@
1
+ # Estágio 4 — Vazamento por resposta
2
+
3
+ Você é um analista de vazamento por resposta.
4
+
5
+ Premissa: toda resposta de erro é informação. Você está
6
+ procurando o que o sistema conta a quem falhou.
7
+
8
+ 1. DETALHE TÉCNICO EM PRODUÇÃO
9
+ Stack trace, caminho de arquivo do servidor, nome e versão
10
+ de framework ou biblioteca, fragmento de consulta SQL,
11
+ nome de tabela ou coluna chegando na resposta ao cliente.
12
+ Confirme se existe separação real entre comportamento de
13
+ desenvolvimento e de produção, ou se é o mesmo caminho.
14
+
15
+ 2. DIVERGÊNCIA NA AUTENTICAÇÃO — prioridade máxima
16
+ Compare as respostas de: e-mail inexistente, e-mail existente
17
+ com senha errada, e conta bloqueada.
18
+ Divergem em mensagem? Em código de status? Em campo do corpo?
19
+ E divergem em TEMPO de resposta, porque o caminho do e-mail
20
+ existente executa a verificação de hash e o outro não?
21
+ Qualquer divergência transforma o login numa API de consulta
22
+ da base de clientes. Marque CRÍTICO.
23
+ O mesmo vale para cadastro ("e-mail já em uso") e recuperação
24
+ de senha.
25
+
26
+ 3. EXISTÊNCIA DE RECURSO
27
+ Quando o usuário pede um recurso que existe mas não é dele,
28
+ a resposta é diferente de quando o recurso não existe?
29
+ Se for, dá para mapear os ids válidos contando respostas.
30
+ O correto é responder "não encontrado" nos dois casos.
31
+
32
+ 4. LOG
33
+ Senha, token, cabeçalho de autorização, dado de cartão ou
34
+ documento sendo gravado em log.
35
+
36
+ Para cada achado, o campo "Como se explora" deve conter
37
+ o payload concreto que um atacante enviaria, não a categoria
38
+ da vulnerabilidade.
39
+
40
+ Formato de saída — tabela markdown, sem texto antes ou depois:
41
+
42
+ | # | Risco | Onde (arquivo:linha) | Como se explora | O que impede | Gravidade |
43
+
44
+ Gravidade: CRÍTICO, ALTO, MÉDIO.
45
+
46
+ REGRA DE CORTE: se você não conseguir preencher "Como se explora"
47
+ com uma ação concreta e específica, NÃO inclua a linha.
48
+ "Poderia ser melhor" não é achado. "Considere adicionar" não é achado.
49
+ Prefiro dez linhas reais a cinquenta linhas de boa prática.
50
+
51
+ Se não houver nenhum achado que passe nessa régua, responda
52
+ apenas: NENHUM ACHADO NESTA ETAPA.
@@ -0,0 +1,27 @@
1
+ # Estágio 5 — Fusão num plano único
2
+
3
+ Vou colar abaixo os relatórios das quatro etapas de auditoria
4
+ (validação de entrada, autorização, abuso por volume, vazamento
5
+ por resposta).
6
+
7
+ Funda os quatro num plano único de correção:
8
+
9
+ 1. Remova duplicatas. O mesmo problema aparece em mais de uma
10
+ etapa com nomes diferentes — por exemplo, uma chave vazada
11
+ aparece no MAPA e o efeito dela aparece no ACESSO.
12
+ Trate como um item só, citando as duas origens.
13
+
14
+ 2. Reordene por ESFORÇO DE CORREÇÃO dividido por GRAVIDADE.
15
+ Primeiro o que é crítico e se resolve em minutos.
16
+ Por último o que é médio e exige refatoração.
17
+
18
+ 3. Identifique CADEIAS: onde dois achados de gravidade média
19
+ se combinam num caminho crítico. Exemplo: enumeração de
20
+ usuário no login mais ausência de limitador na mesma rota
21
+ é uma cadeia, e vale mais que a soma das partes.
22
+
23
+ 4. Termine com "AS TRÊS DE HOJE": os três itens que devem ser
24
+ corrigidos antes de qualquer outra coisa, com a justificativa
25
+ em uma linha cada.
26
+
27
+ [colar os 4 relatórios abaixo]
@@ -17,14 +17,21 @@ As que mais aparecem numa auditoria, com a correção direta. Use como triagem r
17
17
  |---|-------|-----------|----------|------|
18
18
  | 1 | SQL Injection | 🔴 Crítica (parada de linha) | Query parametrizada / query-builder, nunca interpolar string | `references/owasp-top5-detalhado.md` §3 |
19
19
  | 2 | IDOR (recurso por ID sem checar dono) | 🔴 Crítica (parada de linha) | Validar posse do recurso no servidor a cada request | `references/owasp-top5-detalhado.md` §1 |
20
- | 3 | Rate limit ausente (brute force grátis) | 🟠 Alta | Limite por IP **e** por conta + lockout/CAPTCHA no login | `references/headers-rate-limit-cors.md` |
21
- | 4 | CORS refletindo o `Origin` | 🟠 Alta | Allowlist de origens, nunca ecoar o `Origin` recebido | `references/headers-rate-limit-cors.md` |
22
- | 5 | PII/dado demais na resposta (até hash de senha) | 🟠 Alta | Retornar os campos necessários (allowlist de saída) | `references/headers-rate-limit-cors.md` |
23
- | 6 | JWT na URL / token vivo pós-logout | 🟠 Alta | Token no header `Authorization` + revogar no logout | skill `auth-and-secrets` |
24
- | 7 | Enumeração de usuário (erro revela se e-mail existe) | 🟡 Média | Mensagem genérica + resposta em tempo constante | skill `auth-and-secrets` |
25
- | 8 | Clickjacking (sem header) | 🟡 Média (1 min) | `X-Frame-Options: DENY` + CSP `frame-ancestors 'none'` | `references/headers-rate-limit-cors.md` |
26
-
27
- > As duas primeiras (SQLi e IDOR) são **parada de linha**: apareceu, corrige antes de qualquer coisa.
20
+ | 3 | Mass assignment (body inteiro vai pro banco) | 🔴 Crítica (parada de linha) | Allowlist de campos: grave os campos do formulário, nunca `spread` do body | `references/owasp-top5-detalhado.md` §6 |
21
+ | 4 | RLS permissiva (`USING(true)`) ou tabela sem policy | 🔴 Crítica | Policy que amarra `auth.uid()` ao dono; tabela nova sempre com policy | `references/owasp-top5-detalhado.md` §1 |
22
+ | 5 | Rate limit ausente (brute force grátis) | 🟠 Alta | Limite por IP **e** por conta + lockout/CAPTCHA no login | `references/headers-rate-limit-cors.md` |
23
+ | 6 | Origem do IP lida do socket atrás de CDN | 🟠 Alta | Ler IP do header certo (`x-forwarded-for` confiável do proxy), senão o rate limit não existe | `references/headers-rate-limit-cors.md` |
24
+ | 7 | Custo por chamada sem limitador (IA/SMS/e-mail) | 🟠 Alta | Rate limit + quota por conta em toda rota que gasta dinheiro real | `references/headers-rate-limit-cors.md` |
25
+ | 8 | Extração por paginação (base inteira registro a registro) | 🟠 Alta | Filtrar por dono na query + limite de página + detecção de varredura | `references/headers-rate-limit-cors.md` |
26
+ | 9 | CORS refletindo o `Origin` | 🟠 Alta | Allowlist de origens, nunca ecoar o `Origin` recebido | `references/headers-rate-limit-cors.md` |
27
+ | 10 | PII/dado demais na resposta (até hash de senha) | 🟠 Alta | Retornar os campos necessários (allowlist de saída) | `references/headers-rate-limit-cors.md` |
28
+ | 11 | JWT na URL / token vivo pós-logout | 🟠 Alta | Token no header `Authorization` + revogar no logout | skill `auth-and-secrets` |
29
+ | 12 | Enumeração de usuário (erro revela se e-mail existe) | 🟡 Média | Mensagem genérica + resposta em tempo constante | skill `auth-and-secrets` |
30
+ | 13 | Clickjacking (sem header) | 🟡 Média (1 min) | `X-Frame-Options: DENY` + CSP `frame-ancestors 'none'` | `references/headers-rate-limit-cors.md` |
31
+
32
+ > As quatro primeiras (SQLi, IDOR, mass assignment e RLS permissiva) são **parada de linha**: apareceu, corrige antes de qualquer coisa.
33
+ >
34
+ > Para uma auditoria adversarial completa (caça com prova de exploração + plano priorizado), use a skill `security-audit-pentest`. Esta skill aqui é para **corrigir** uma falha específica da tabela.
28
35
 
29
36
  ## References (load on demand)
30
37
 
@@ -56,6 +56,17 @@ async headers() {
56
56
  - Comparações de token/secret: `crypto.timingSafeEqual`, nunca `!==` (detalhe em auth-and-secrets)
57
57
  - Sem rate limit = brute force de graça. Somar lockout/CAPTCHA após N falhas no login
58
58
 
59
+ ### Origem do IP atrás de proxy/CDN
60
+ Se o app roda atrás de Vercel/Cloudflare/qualquer proxy, o IP do socket é o do proxy, não o do cliente. Ler o socket direto faz **o mundo inteiro contar como um IP só** e o rate limit deixa de existir na prática.
61
+ - Vercel: use `x-forwarded-for` (ou `req.ip` no runtime que já resolve). Pegue o **primeiro** IP da lista, mas só confie na cadeia que vem do seu proxy.
62
+ - Nunca confie em `x-forwarded-for` cru vindo de origem não-proxy (o cliente falsifica o header). Configure a lib de rate limit para o número certo de proxies à frente.
63
+
64
+ ### Custo por chamada (rota que gasta dinheiro real)
65
+ Toda rota que chama modelo de linguagem, dispara SMS, envia e-mail, processa arquivo ou bate em API paga de terceiro precisa de rate limit **e** quota por conta. Rota de agente de IA exposta sem limitador é crítica mesmo sem vazar dado nenhum: 10.000 chamadas em loop viram conta de centenas a milhares de reais/dólares. Estime o custo unitário e multiplique por 10k ao avaliar a rota.
66
+
67
+ ### Extração por paginação
68
+ Rota que devolve um registro por vez e, chamada em sequência, entrega a base inteira. Barre com: filtro por dono na query (o mesmo do IDOR), limite máximo de página, e detecção de varredura (muitos ids sequenciais do mesmo cliente).
69
+
59
70
  ## CORS
60
71
  - **Nunca reflita o header `Origin` recebido** de volta em `Access-Control-Allow-Origin` (isso libera qualquer site a chamar sua API com credenciais)
61
72
  - Use uma **allowlist explícita** de origens: compare o `Origin` recebido contra a lista e só então ecoe o valor exato daquela origem
@@ -28,6 +28,38 @@ export async function GET(req: Request, { params }: { params: Promise<{ id: stri
28
28
  }
29
29
  ```
30
30
 
31
+ ### RLS: policy permissiva é pior que policy nenhuma
32
+ Ler os arquivos de migration/policy versionados e checar:
33
+ - Toda tabela de domínio tem `ENABLE ROW LEVEL SECURITY`?
34
+ - Nenhuma policy é permissiva: `USING (true)` ou `WITH CHECK (true)` libera geral, mas o painel do Supabase mostra a trava como "ativa". Amarre sempre ao dono: `USING (auth.uid() = user_id)`.
35
+ - Tabela criada em migration recente **sem** policy correspondente = exposta.
36
+
37
+ ```sql
38
+ -- ERRADO: mostra RLS ativo no painel, mas libera qualquer um
39
+ create policy "read" on projects for select using (true);
40
+
41
+ -- CERTO: amarra o dono
42
+ alter table projects enable row level security;
43
+ create policy "own rows" on projects
44
+ for select using (auth.uid() = user_id);
45
+ ```
46
+
47
+ > RLS de verdade é estado do banco, não arquivo. O que está no repo é ponto de partida: confirme o estado real no dashboard/`\d+` do Postgres.
48
+
49
+ ## 6. Mass Assignment (atribuição em massa)
50
+ O corpo da requisição inteiro repassado para o banco (`spread` do objeto, `update`/`create` com o payload completo) deixa o atacante setar qualquer coluna, mesmo as que não estão no formulário. Alvos clássicos: `role`, `is_admin`, `plan`, `balance`, `payment_status`, `user_id`/`owner_id`.
51
+
52
+ ```ts
53
+ // ERRADO: o cliente manda { name: "x", role: "admin" } e vira admin
54
+ await supabase.from("users").update({ ...body }).eq("id", userId)
55
+
56
+ // CERTO: allowlist explícita, só os campos do formulário
57
+ const { name, bio } = updateSchema.parse(body) // Zod com só os campos permitidos
58
+ await supabase.from("users").update({ name, bio }).eq("id", userId)
59
+ ```
60
+
61
+ Regra: **nunca** `...body` / `...req.body` num write. Sempre desestruture (ou valide com um schema que só contém os campos editáveis). Campos de papel, permissão, plano, saldo, status de pagamento e id de dono nunca vêm do cliente.
62
+
31
63
  ## 2. Cryptographic Failures
32
64
  - HTTPS em tudo, sem exceção
33
65
  - HSTS header com includeSubDomains
@@ -15,6 +15,7 @@ Use esta tabela **só fora de projeto Wizz** (sem `_wizz/`), quando o router map
15
15
  | Auth, secrets, tokens, OAuth, JWT, Clerk, permissões | `auth-and-secrets` + `web-security` | 1 |
16
16
  | Dependências, packages, vulnerabilidades, npm audit | `database-and-deps` | 2 |
17
17
  | Segurança, XSS, CSRF, SQLi, IDOR, OWASP, rate limit, CORS, clickjacking, PII na resposta, enumeração de usuário, headers | `web-security` + `auth-and-secrets` | 1 |
18
+ | Auditoria/pentest de segurança do app inteiro, "auditar segurança", "pentest", "encontrar vulnerabilidades", varredura antes de release | `security-audit-pentest` | 1 |
18
19
  | Desktop, Electron, contextIsolation, code signing | `desktop-security` | 2 |
19
20
 
20
21
  ## Área Técnica — Código e Qualidade