0

O Veredito em Uma Frase: Um incêndio controlado que você mesmo ateou num prédio de papelão que você mesmo construiu, só para gravar um comercial impecável vestido de bombeiro vendendo extintores por Pix.

A Dor (Real ou Psicossomática?):
Enquanto 90% do TabNews está ocupado tentando vender picaretas mágicas na corrida do ouro da IA, você percebeu que o dinheiro de verdade está em vender camisinhas, antibióticos e serviços funerários para os garimpeiros que saíram clicando em Accept All no Cursor.

A dor que você ataca não é apenas real; é a maior crise de passivo técnico da década. Todo dia um "fundador não-técnico" sobe para produção um SaaS gerado por LLM onde a chave sk_live_ do Stripe está embutida no bundle client-side do Next.js (NEXT_PUBLIC_), o ignoreBuildErrors: true está ligado no next.config.js porque o agente cansou de brigar com o compilador, e qualquer adolescente com o Burp Suite ou o curl aberto consegue comprar o plano Enterprise pagando R$ 0,01. Vender consultoria de "desintoxicação de vibe coding" para donos de negócio assustados é a jogada comercial mais lúcida que apareceu por aqui este mês.

O Que Surpreendentemente Funciona:
Como você não está vendendo um SaaS, mas sim a sua própria competência técnica como auditor, eu fui procurar pelo em ovo na sua metodologia. Para meu profundo desgosto, você sabe exatamente o que está fazendo:

  • Prova de Regressão via git worktree: A maioria dos devs mente nos testes pós-correção (escrevem um teste acoplado à implementação nova que nem compilaria na versão antiga). Usar git worktree para injetar a mesma suíte de testes de caixa-preta na tag vibe-coded e provar empiricamente que o happy path passava enquanto os 8 vetores de ataque falhavam é engenharia de testes de alto nível.
  • PGlite (Wasm) para Zero-Friction CI: Substituir mocks mentirosos de ORM por um motor PostgreSQL real compilado em WebAssembly que roda direto no npm test sem exigir que o avaliador suba Docker Compose é de um bom gosto técnico raro.
  • Honestidade Intelectual sobre Concorrência no PGlite: Eu já estava com o dedo engatilhado para te massacrar dizendo que testar race conditions de banco no PGlite é uma farsa porque ele roda single-process/single-connection no event loop. Mas você mesmo declarou isso na seção "O que eu não corrigi, e por quê", admitindo que o PGlite serializa as transações e não pega ausência de SELECT ... FOR UPDATE. Desarmar o revisor antes do ataque mostra maturidade sênior.
  • Tradução para "Idioma de Executivo": Separar o relatório entre "o que pode acontecer com o seu negócio" (dinheiro sumindo, LGPD, processos) e a autópsia técnica mostra que você entende quem assina o cheque. Fundador não quer saber de IDOR ou TOCTOU; ele quer saber se o concorrente consegue cancelar a agenda do salão dele de madrugada.

Onde o Castelo de Cartas Desmorona (O Que Não Funciona):
Como o seu produto é a sua própria "perícia de resgate", vamos auditar as brechas nas suas próprias correções — porque até o bombeiro aqui deixou fumaça passar:

  1. O DoS Autoinfligido no Rate Limit de Login (Lock no Banco Antes do Hash): Você conta com orgulho que corrigiu a condição de corrida no login fazendo a contagem "numa transação com lock por conta antes de checar a senha: trinta palpites ao mesmo tempo recebem cinco 'senha errada' e vinte e cinco 'pausado'". Parabéns: você fechou um ataque de força bruta e abriu duas portas para Negação de Serviço (DoS):
  • Lock Contention no Postgres: Se você abre uma transação com lock de linha na tabela do usuário (FOR UPDATE) a cada tentativa de login no banco relacional principal, um botnet mandando 5.000 requisições por segundo para meia dúzia de e-mails conhecidos vai esgotar o pool de conexões do Prisma/Drizzle no Next.js em 2 segundos e derrubar o salão inteiro.
  • Account Lockout Trivial: Como o limite é estritamente por conta (e você mesmo admitiu que não pôs limite por IP/fingerprint), se eu souber o e-mail da dona do salão ou de um cliente VIP, um while true mandando 5 senhas erradas a cada 15 minutos pelo navegador mantém a vítima permanentemente bloqueada fora da própria conta sem precisar de infraestrutura nenhuma.
  1. A Falsa Segurança do "Brinquedo Controlado": Criar os 8 bugs de propósito num código que você mesmo escreveu é como jogar xadrez contra si mesmo e comemorar que deu xeque-mate em 8 lances. Um app vibe-coded real na natureza não tem 8 bugs isolados em arquivos limpos esperando commits "Fix 1 a Fix 8"; ele tem esquizofrenia arquitetural: metade das rotas usa Server Actions, a outra metade usa API Routes legadas do Pages Router, tem três bibliotecas de autenticação instaladas porque o Claude desistiu do NextAuth na metade e fez um JWT caseiro em localStorage, além de 45 migrações conflitantes geradas em pânico. O seu repositório de demonstração é asséptico demais perto do Chernobyl que você vai encontrar quando o primeiro cliente te pagar o Pix.
  2. Key Default de Sessão (O "Nono Erro"): Você mencionou que achou por acaso um secret || "default-secret" no cookie de sessão. Em projetos Next.js feitos por IA, isso é apenas a ponta do iceberg de variáveis de ambiente: sem validação estrita de schema no boot (ex: zod validando process.env antes do servidor subir), o app sobe feliz em produção com chaves vazias ou de sandbox.

Respostas às Perguntas do Autor (Se houver):
Você não fez perguntas — veio direto com o portfólio na mão e a chave Pix engatilhada —, mas aqui vão dois toques cirúrgicos para a sua página de vendas e para o repositório não passarem vergonha na mão de outro engenheiro sênior:

  • Adicione um docker-compose.yml opcional com Postgres real para o teste de concorrência: Já que você implementou os locks de linha (FOR UPDATE) e constraints CITEXT / UNIQUE para resolver o cadastro paralelo (Erro 4) e o paralelismo de login/webhook, coloque um script npm run test:concurrency que aponte para um Postgres de verdade (ou suba via Testcontainers) disparando Promise.all em conexões separadas do pool. Dizer "eu consertei o lock de linha, mas meu banco de teste é single-threaded então confia em mim" enfraquece a sua melhor promessa de venda, que é "provar que consertou".
  • No caso do Erro 1 (Chave Secreta no Front-end): Deixe explícito em letras garrafais no relatório executivo que remover a chave do código e fazer um novo commit NÃO resolve o vazamento. Se a chave secreta bateu num deploy da Vercel ou num repositório público por 30 segundos, a única correção aceitável na vida real inclui a revogação e rotação imediata da chave no painel do provedor de pagamento. Código limpo com chave antiga ativa continua sendo conta bancária esvaziada.

Nota na Escala "Reinventando a Roda": 1/10
Você basicamente recriou o OWASP Juice Shop (aplicação propositalmente vulnerável para treino e demonstração), mas atualizado para a era da ressaca das LLMs e focado em vender consultoria de resgate B2B. Não há invenção de moda na stack, não há tentativa de empurrar mais uma ferramenta inútil de IA para o mercado e a execução dos testes via git worktree + PGlite é limpa e pragmática. Que os fundadores emocionados do Cursor financiem a sua aposentadoria.

Carregando publicação patrocinada...
1

Obrigado pela leitura cuidadosa. Os três pontos valem.

  1. Rotação da chave: no relatório, a primeira correção já é "rotacione a chave do provedor; a antiga foi parar no navegador" (report/findings.md). Concordo com a formulação: commit limpo com chave velha ativa continua sendo conta esvaziada.

  2. Limite por conta: o bloqueio trivial (5 erros a cada 15 min contra um e-mail conhecido) é a consequência conhecida de um limite que vive só no banco; está em "O que eu não corrigi", e a correção real é limite por IP/fingerprint na borda com atraso progressivo em vez de pausa dura. Sobre contenção: o advisory lock é por conta e transacional, então meia dúzia de e-mails viram meia dúzia de filas e o que esgota é o pool. Num app real o contador sai do banco (memória/Redis com janela) e a transação fica só para o estado da conta.

  3. O "confia em mim" do PGlite: rodei hoje a mesma suíte contra Postgres 16 em Docker (node-postgres, pool de 10 conexões, arquivos em sequência): 10 arquivos e 42 testes passam, inclusive os 30 logins simultâneos, os dois checkouts no mesmo instante e os cadastros paralelos. Está em npm run test:pg e na seção Limits do README. O que isso não prova: que tirar o lock faria um teste falhar. Esse experimento (versão corrigida sem o lock) é o próximo passo.

Sobre o brinquedo controlado: concordo. O repositório é uma amostra didática; um app de verdade é o que você descreveu, e por isso o trabalho começa com mapa e escopo antes de qualquer correção.