StingwardAuditoria de segurança

Auditoria de segurança do projeto

my-saas

Há problemas graves. Corrija os itens críticos e altos antes de lançar ou de colocar mais usuários no app.

Alvo
./my-saas
Data
22 de outubro de 2026
Tipo
Projeto
Achados
4 críticos, 6 altos, 7 médios, 1 baixo
Ferramenta
Stingward 0.1.7

Gerado pelo Stingward 0.1.7, software livre (Apache 2.0). github.com/HiveControlAgents/StingWard

1Resumo executivo

NotaF

O que a nota quer dizer

A nota vai de A a F e depende só dos achados mais graves.

  1. ASó itens baixos, ou nenhum.
  2. BItens médios, nenhum alto ou crítico.
  3. CUm ou dois itens altos, nenhum crítico.
  4. DTrês ou mais itens altos, nenhum crítico.
  5. FPelo menos um item crítico.

18 achados, agrupados em 16 problemas: o mesmo problema em vários lugares aparece uma vez, com todos os lugares.

Corrija primeiro

  1. Qualquer visitante do site pode copiar NEXT_PUBLIC_OPENAI_API_KEY e usar como se fosse você: estourar a fatura do serviço ou chegar aos dados que a chave protege.

    Onde:.env:1app/api/chat/route.ts:3

  2. Uma instrução escondida numa página, num arquivo ou numa mensagem que o modelo leia vira comando no seu servidor ou no navegador dos seus usuários.

    Onde:app/api/agent/route.ts:16

  3. Esta versão de next tem uma falha pública e documentada: quem ataca não precisa descobrir nada, só reaproveitar um ataque pronto. Chance de a CVE-2025-29927 ser explorada nos próximos 30 dias: 99% (EPSS).

    Onde:package-lock.json:15

2Plano de correção

Siga de cima para baixo. O esforço é uma estimativa para quem conhece o código.

Rápido: menos de uma hora. Horas: cabe num dia. Dias: mais de um dia, em geral porque é preciso testar de novo ou reescrever uma parte.

#ProblemaOndeEsforço
Crítico: corrija agora
1Segredo exposto no navegador: NEXT_PUBLIC_OPENAI_API_KEY.env:1app/api/chat/route.ts:3Horas
2Texto escrito pelo modelo chega a um comando de shellapp/api/agent/route.ts:16Horas
3next 14.1.0: 36 falhas conhecidas; a pior é CVE-2025-29927 (Authorization Bypass in Next.js Middleware)package-lock.json:15Dias
Alto: nesta semana
4SDK de IA rodando no navegador (dangerouslyAllowBrowser)app/api/chat/route.ts:3Horas
5Tabela profiles sem RLS no Supabasesupabase/migrations/0001_profiles.sql:1Horas
6Consulta SQL montada com dados da requisiçãoapp/api/invoices/route.ts:7Horas
7Caminho de arquivo montado com dados da requisição (fs.readFileSync())app/api/invoices/pdf/route.ts:6Horas
8Servidor busca uma URL que veio da requisição (fetch())app/api/preview/route.ts:4Horas
9axios 1.6.0: 31 falhas conhecidas; a pior é CVE-2026-25639 (Axios is Vulnerable to Denial of Service via __proto__ Key in mergeConfig)package-lock.json:19Horas
Médio: neste mês
10Arquivo .env fora do .gitignore.envRápido
11Política do Supabase liberada para todos: read invoices (select)supabase/migrations/0001_profiles.sql:9Horas
12CORS aberto para qualquer siteapp/api/chat/route.ts:8Rápido
13Texto escrito pelo modelo chega à página como HTMLcomponents/Chat.tsx:6Horas
14Rota de IA sem login e sem limite de usoapp/api/agent/route.ts:7app/api/chat/route.ts:5Horas
15requests >=2.19.0: a menor versão aceita (2.19.0) tem 5 falhas conhecidas; a pior é CVE-2018-18074 (Insufficiently Protected Credentials in Requests)worker/requirements.txt:2Horas
Baixo: quando puder
16requirements.txt sem lockfile: 2 dependência(s) com versão abertaworker/requirements.txtRápido

3Achados em detalhe

Trechos lidos dos arquivos no momento da auditoria. Segredos aparecem mascarados.

Aplicação 8

#1CríticoRegra APP001

Segredo exposto no navegador: NEXT_PUBLIC_OPENAI_API_KEY

Onde
  • .env:1
  • app/api/chat/route.ts:3
Evidência
.env linha 1
1NEXT_PUBLIC_OPENAI_API_KEY=[REDACTED:secret_field]
app/api/chat/route.ts linha 3
2
3const openai = new OpenAI({ apiKey: process.env.NEXT_PUBLIC_OPENAI_API_KEY, dangerouslyAllowBrowser: true });
4
Por que importa

Variáveis com prefixo NEXT_PUBLIC_ vão para o JavaScript que o navegador baixa. Qualquer visitante vê o valor.

Como corrigir

Tire o prefixo NEXT_PUBLIC_, use a variável só no servidor (API route, server action, edge function) e troque a chave.

Padrões
#2CríticoRegra APP009

Texto escrito pelo modelo chega a um comando de shell

Onde
  • app/api/agent/route.ts:16
Evidência
app/api/agent/route.ts linha 16
15 });
16 exec(reply.choices[0].message.content ?? "");
17 return Response.json({ ok: true });
Por que importa

A resposta do modelo pode ser guiada por qualquer coisa que ele leu (uma página, um arquivo, a mensagem de alguém). Usar isso direto transforma uma injeção de prompt em comandos rodando no seu servidor.

Como corrigir

Trate a saída do modelo como entrada de usuário: mapeie a resposta para comandos de uma lista fixa e passe os argumentos em lista, sem shell.

Padrões
#4AltoRegra APP002

SDK de IA rodando no navegador (dangerouslyAllowBrowser)

Onde
  • app/api/chat/route.ts:3
Evidência
app/api/chat/route.ts linha 3
2
3const openai = new OpenAI({ apiKey: process.env.NEXT_PUBLIC_OPENAI_API_KEY, dangerouslyAllowBrowser: true });
4
Por que importa

A chave da OpenAI/Anthropic vai junto para o navegador. Alguém copia e usa sua conta até estourar a fatura.

Como corrigir

Chame a API de IA por uma rota no servidor e deixe a chave só lá.

Padrões
#5AltoRegra APP004

Tabela profiles sem RLS no Supabase

Onde
  • supabase/migrations/0001_profiles.sql:1
Evidência
supabase/migrations/0001_profiles.sql linha 1
1create table public.profiles (
2 id uuid primary key references auth.users,
Por que importa

Sem Row Level Security, qualquer pessoa com a chave anon (que é pública) lê e altera essa tabela.

Como corrigir

Adicione: alter table profiles enable row level security; e crie políticas para cada operação.

Padrões
#11MédioRegra APP005

Política do Supabase liberada para todos: read invoices (select)

Onde
  • supabase/migrations/0001_profiles.sql:9
Evidência
supabase/migrations/0001_profiles.sql linha 9
8alter table public.invoices enable row level security;
9create policy "read invoices" on public.invoices for select using (true);
Por que importa

A política usa true como condição, então vale para qualquer pessoa, logada ou não.

Como corrigir

Troque true por uma condição como auth.uid() = user_id.

Padrões
#12MédioRegra APP003

CORS aberto para qualquer site

Onde
  • app/api/chat/route.ts:8
Evidência
app/api/chat/route.ts linha 8
7 const reply = await openai.chat.completions.create({ model: "gpt-4o-mini", messages: [{ role: "user", content: prompt }] });
8 return new Response(JSON.stringify(reply), { headers: { "Access-Control-Allow-Origin": "*" } });
9}
Por que importa

Qualquer página da internet pode chamar sua API pelo navegador de quem visitar.

Como corrigir

Liste só os domínios do seu front-end em origin/allow_origins.

Padrões
#13MédioRegra APP009

Texto escrito pelo modelo chega à página como HTML

Onde
  • components/Chat.tsx:6
Evidência
components/Chat.tsx linha 6
5 const { messages } = useChat();
6 return messages.map((m) => <div key={m.id} dangerouslySetInnerHTML={{ __html: m.content }} />);
7}
Por que importa

A resposta do modelo pode ser guiada por qualquer coisa que ele leu (uma página, um arquivo, a mensagem de alguém). Usar isso direto transforma uma injeção de prompt em scripts rodando no navegador de quem usa o app.

Como corrigir

Trate a saída do modelo como entrada de usuário: mostre como texto, ou converta o Markdown e limpe com DOMPurify.sanitize antes de inserir.

Padrões
#14MédioRegra APP011

Rota de IA sem login e sem limite de uso

Onde
  • app/api/agent/route.ts:7
  • app/api/chat/route.ts:5
Evidência
app/api/agent/route.ts linha 7
6// The user describes a chore in plain words and the agent does it on the server.
7export async function POST(req: Request) {
8 const { task } = await req.json();
app/api/chat/route.ts linha 5
4
5export async function POST(req: Request) {
6 const { prompt } = await req.json();
Por que importa

Quem achar essa URL chama o modelo na sua conta em loop: a fatura dispara e o seu app vira proxy de IA de graça para outras pessoas.

Como corrigir

Exija usuário logado, limite pedidos por usuário ou IP (@upstash/ratelimit, express-rate-limit, slowapi), defina max_tokens / maxOutputTokens e ponha um limite de gasto no painel do provedor.

Padrões

Dependências 4

#3CríticoRegra DEP001

next 14.1.0: 36 falhas conhecidas; a pior é CVE-2025-29927 (Authorization Bypass in Next.js Middleware)

Onde
  • package-lock.json:15
Evidência
package-lock.json linha 15
14 },
15 "node_modules/next": {
16 "version": "14.1.0",
Por que importa

Esta versão tem falhas públicas. Outras: CVE-2024-46982, CVE-2026-75604, GHSA-2464-fhr4-w9xw, GHSA-2xp9-vwfh-vxw4, CVE-2024-34351 e mais 30. Chance de a CVE-2025-29927 ser explorada nos próximos 30 dias: 99% (EPSS).

Como corrigir

Atualize next para 15.5.28 ou mais nova.

Falhas conhecidas nesta versão
IDSeveridadeResumoCorrigida em
CVE-2025-29927CríticoAuthorization Bypass in Next.js MiddlewareEPSS 99%14.2.25
CVE-2024-46982CríticoNext.js Cache PoisoningEPSS 59%14.2.10
CVE-2026-75604CríticoNext.js: Unauthenticated Remote Code Execution on windows-hosted serversEPSS 2%15.5.24
GHSA-2464-fhr4-w9xwCríticoNext.js: Unauthenticated Remote Code Execution in Image Optimization API when SVG files...15.5.28
GHSA-2xp9-vwfh-vxw4CríticoNext.js: Unauthenticated Remote Code Execution in Image Optimization API when AVIF file...15.5.24
CVE-2024-34351AltoNext.js Server-Side Request Forgery in Server ActionsEPSS 5%14.1.1
CVE-2024-51479AltoNext.js authorization bypass vulnerabilityEPSS 4%14.2.15
CVE-2026-44578AltoNext.js vulnerable to server-side request forgery in applications using WebSocket upgradesEPSS 2%15.5.16

e mais 28 falhas

Padrões
#9AltoRegra DEP001

axios 1.6.0: 31 falhas conhecidas; a pior é CVE-2026-25639 (Axios is Vulnerable to Denial of Service via __proto__ Key in mergeConfig)

Onde
  • package-lock.json:19
Evidência
package-lock.json linha 19
18 },
19 "node_modules/axios": {
20 "version": "1.6.0",
Por que importa

Esta versão tem falhas públicas. Outras: CVE-2024-39338, CVE-2025-58754, CVE-2026-44495, CVE-2026-44496, CVE-2026-44494 e mais 25.

Como corrigir

Atualize axios para 1.20.0 ou mais nova.

Falhas conhecidas nesta versão
IDSeveridadeResumoCorrigida em
CVE-2026-25639AltoAxios is Vulnerable to Denial of Service via __proto__ Key in mergeConfigEPSS 2%1.13.5
CVE-2024-39338AltoServer-Side Request Forgery in axiosEPSS 1%1.7.4
CVE-2025-58754AltoAxios is vulnerable to DoS attack through lack of data size checkEPSS 1%1.12.0
CVE-2026-44495Altoaxios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollution Gad...EPSS 1%1.15.2
CVE-2026-44496AltoAxios: Regular Expression Denial of Service (ReDoS) via Cookie Name Injection1.16.0
CVE-2026-44494Altoaxios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `config.pr...1.16.0
CVE-2026-42033AltoAxios: Prototype Pollution Gadgets - Response Tampering, Data Exfiltration, and Request...1.15.1
CVE-2025-27152Altoaxios Requests Vulnerable To Possible SSRF and Credential Leakage via Absolute URL1.8.2

e mais 23 falhas

Padrões
#15MédioRegra DEP004

requests >=2.19.0: a menor versão aceita (2.19.0) tem 5 falhas conhecidas; a pior é CVE-2018-18074 (Insufficiently Protected Credentials in Requests)

Onde
  • worker/requirements.txt:2
Evidência
worker/requirements.txt linha 2
1openai>=1.30
2requests>=2.19.0
Por que importa

Sem lockfile, a faixa é a única regra: quem já tem a versão 2.19.0 instalada continua com ela, porque ela cabe na faixa, e a instalação pode escolhê-la para atender outros pacotes. Outras falhas na versão 2.19.0: CVE-2023-32681, CVE-2024-47081, CVE-2024-35195, CVE-2026-25645.

Como corrigir

Suba a versão mínima para 2.33.0: troque requests>=2.19.0 por requests>=2.33.0. Na próxima instalação, quem tem versão antiga recebe a nova.

Falhas conhecidas na menor versão aceita
IDSeveridadeResumoCorrigida em
CVE-2018-18074AltoInsufficiently Protected Credentials in RequestsEPSS 7%2.20.0
CVE-2023-32681MédioUnintended leak of Proxy-Authorization header in requestsEPSS 3%2.31.0
CVE-2024-47081MédioRequests vulnerable to .netrc credentials leak via malicious URLs2.32.4
CVE-2024-35195MédioRequests `Session` object does not verify requests after making first request with veri...2.32.0
CVE-2026-25645MédioRequests has Insecure Temp File Reuse in its extract_zipped_paths() utility function2.33.0
Padrões
#16BaixoRegra DEP003

requirements.txt sem lockfile: 2 dependência(s) com versão aberta

Onde
  • worker/requirements.txt (o arquivo inteiro)
Por que importa

Sem lockfile não dá para saber qual versão está instalada, nem para auditar. Cada instalação pode puxar uma versão diferente.

Como corrigir

Gere e faça commit do lockfile (npm install, poetry lock, uv lock) ou fixe as versões com ==.

Padrões

Código 3

#6AltoRegra COD001

Consulta SQL montada com dados da requisição

Onde
  • app/api/invoices/route.ts:7
Evidência
app/api/invoices/route.ts linha 7
6 const user = new URL(req.url).searchParams.get("user");
7 const { rows } = await pool.query(`select * from invoices where user_id = '${user}'`);
8 return Response.json(rows);
Por que importa

O valor entra no meio do texto do SQL. Quem manda algo como ' OR '1'='1 consegue ler, mudar ou apagar dados do banco.

Como corrigir

Passe os valores como parâmetros e deixe o driver juntar: cursor.execute("... WHERE id = %s", (id,)), $1 no Postgres, ? no SQLite, sql`...` ou $queryRaw`...` no Prisma. Nome de coluna ou de tabela não pode ser parâmetro: escolha o nome numa lista fixa (SORTS[chave], if order not in ALLOWED: abort(400)).

Padrões
#7AltoRegra COD007

Caminho de arquivo montado com dados da requisição (fs.readFileSync())

Onde
  • app/api/invoices/pdf/route.ts:6
Evidência
app/api/invoices/pdf/route.ts linha 6
5 const file = new URL(req.url).searchParams.get("file") ?? "";
6 const pdf = fs.readFileSync(path.join(process.cwd(), "invoices", file));
7 return new Response(pdf, { headers: { "Content-Type": "application/pdf" } });
Por que importa

Um valor como ../../.env deixa alguém ler ou sobrescrever qualquer arquivo que o servidor alcança.

Como corrigir

Use só o nome do arquivo (os.path.basename, secure_filename, path.basename) ou resolva o caminho e confira que ele continua dentro da pasta esperada.

Padrões
#8AltoRegra COD008

Servidor busca uma URL que veio da requisição (fetch())

Onde
  • app/api/preview/route.ts:4
Evidência
app/api/preview/route.ts linha 4
3 const { url } = await req.json();
4 const page = await fetch(url);
5 const html = await page.text();
Por que importa

Alguém pode fazer o seu servidor chamar endereços internos, como o serviço de metadados da nuvem (169.254.169.254), e devolver a resposta com as credenciais.

Como corrigir

Aceite só os domínios de que você precisa, bloqueie endereços privados e locais e não siga redirecionamentos sem conferir.

Padrões

Segredos 1

#10MédioRegra SEC003

Arquivo .env fora do .gitignore

Onde
  • .env (o arquivo inteiro)
Por que importa

O arquivo ainda não foi commitado, mas o próximo git add . leva as chaves junto.

Como corrigir

Adicione .env ao .gitignore.

Padrões

4Escopo e método

O que foi verificado

Bases consultadas

BasePara quê Situação
OSV.devfalhas conhecidas e pacotes maliciososconsultado
CISA KEVfalhas que atacantes estão explorando agoraconsultado, catálogo 2026.10.21
EPSS (FIRST)chance de exploração nos próximos 30 diasconsultado
NVDnota de severidade quando o aviso não traznão foi preciso: todos os avisos tinham nota

Só nomes e versões de pacotes saíram da máquina, para essas bases. O código não saiu.

Como foi feito

  1. Listamos os arquivos do alvo, sem as pastas de dependências e de build, como node_modules e dist. Numa skill de terceiros o build é lido, porque é ele que roda.
  2. Lemos os lockfiles e perguntamos ao OSV.dev sobre cada pacote e versão. Falhas que estão na lista KEV da CISA viram críticas; EPSS e NVD completam a chance de exploração e as notas que faltam.
  3. Rodamos 51 regras para segredos, erros comuns de apps feitos com IA, falhas no código do próprio projeto, configurações de agentes e MCP e conteúdo de skills. Pastas de teste, documentação e exemplos pesam menos.
  4. Damos a nota de A a F pelos achados mais graves.

Limites