🔒 AUDITORIA DE SEGURANÇA COMPLETA — rv-adv

Data: 18/04/2026 | Auditor: Automated Security Review Versão do Código: current | Severidade: Ver tabela abaixo


1.1 AUTENTICAÇÃO E AUTORIZAÇÃO


FINDING 1.1.1 [CRÍTICA]

Severidade: 🔴 CRÍTICA Localização: supabase/functions/_shared/auth.ts:136-137 CWE: CWE-345 (Insufficient Verification of Data Authenticity)

Descrição: Tokens HS256 são decodificados SEM verificação de assinatura.

Evidência:

// Linha 136-137
if (alg === "HS256") {
  const payload = decodePayloadUnsafe(token);  // ⚠️ Sem verificação de assinatura!
  if (!payload) return null;

O código apenas decodifica o payload Base64 sem verificar a assinatura HMAC-SHA256. Qualquer atacante que consiga forjar um JWT com role="service_role" ou role="authenticated" e exp adequadamente terá acesso total.

Recomendação:

  1. Usar jose.jwtVerify() com o JWT_SECRET para tokens HS256
  2. Alternativamente, rejeitar tokens HS256 e forçar migração para ES256
  3. Documentar claramente o risco se HS256 for aceito sem verificação

Status: ⚠️ REQUER CORREÇÃO IMEDIATA


FINDING 1.1.2 [CRÍTICA]

Severidade: 🔴 CRÍTICA Localização: supabase/functions/delete-google-calendar/index.ts:1-63 CWE: CWE-306 (Missing Authentication for Critical Function)

Descrição: Edge Function SEM qualquer autenticação. Qualquer usuário pode deletar eventos do Google Calendar.

Evidência:

// Não há authenticateRequest, nenhuma verificação de JWT
serve(async (req) => {
  if (req.method === "OPTIONS") {
    return new Response("ok", { headers: corsHeaders });
  }

  try {
    const { event_id } = await req.json();
    // ⚠️ Qualquer pessoa pode deletar QUALQUER event_id!

Recomendação:

  1. Adicionar authenticateRequest no início da função
  2. Validar que o user_id do token corresponde ao proprietário do evento
  3. Ou implementar HMAC signature validation para webhooks

Status: ⚠️ REQUER CORREÇÃO IMEDIATA


FINDING 1.1.3 [CRÍTICA]

Severidade: 🔴 CRÍTICA Localização: supabase/functions/sync-google-calendar/index.ts:1-143 CWE: CWE-306 (Missing Authentication for Critical Function)

Descrição: Edge Function SEM autenticação. Qualquer pessoa pode sincronizar eventos.

Evidência:

// Sem verificação de autenticação
serve(async (req) => {
  if (req.method === "OPTIONS") {
    return new Response("ok", { headers: getCorsHeaders(req.headers.get("origin")) });
  }
  // ⚠️ Qualquer pessoa pode criar/atualizar eventos no Google Calendar!
  const { pericia_id } = await req.json();

Recomendação:

  1. Adicionar authenticateRequest no início da função
  2. Validar que o user_id do token tem permissão para modificar a perícia

Status: ⚠️ REQUER CORREÇÃO IMEDIATA


FINDING 1.1.4 [ALTA]

Severidade: 🟠 ALTA Localização: supabase/functions/scrape-tnu/index.ts:319-326 CWE: CWE-287 (Improper Authentication)

Descrição: Autenticação fraca - apenas verifica presença do header Authorization, não valida o JWT.

Evidência:

const authHeader = req.headers.get("authorization") ?? "";
const jwt = authHeader.replace(/^Bearer\s+/i, "");
if (!jwt) {
  return new Response(JSON.stringify({ error: "Unauthorized" }), {
    status: 401,
    // ⚠️ Apenas verifica se existe Bearer token, NÃO valida o token!

Recomendação:

  1. Usar authenticateRequest do módulo _shared/auth.ts
  2. OU implementar verificação completa do JWT

Status: ⚠️ REQUER CORREÇÃO


FINDING 1.1.5 [MÉDIA]

Severidade: 🟡 MÉDIA Localização: supabase/functions/import-tramita-clients/index.ts:267 CWE: CWE-295 (Improper Certificate Validation)

Descrição: Bug de copiar/colar - usa SERVICE_ROLE_KEY onde deveria usar ANON_KEY.

Evidência:

// Linha 267
const supabaseAnonKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "";  // ⚠️ ERRADO!
// Deveria ser:
const supabaseAnonKey = Deno.env.get("SUPABASE_ANON_KEY") ?? "";

Isto expõe o service_role key ao código cliente, comprometendo RLS.

Recomendação:

  1. Corrigir para Deno.env.get("SUPABASE_ANON_KEY")
  2. Testar autenticação JWT após correção

Status: ⚠️ REQUER CORREÇÃO


FINDING 1.1.6 [MÉDIA]

Severidade: 🟡 MÉDIA Localização: supabase/functions/_shared/rate-limit.ts CWE: CWE-837 (Insufficient Rate Limit)

Descrição: Rate limiting em memória é ineficaz em Edge Functions serverless.

Evidência:

// Cada isolate tem seu próprio Map - rate limits não são compartilhados
const rateLimits = new Map<string, { count: number; expiresAt: number }>();

Em ambiente Deno Deploy/Edge, cada requisição pode executar em isolate diferente, invalidando o rate limiting.

Recomendação:

  1. Usar Redis ou similar para rate limiting distribuído
  2. Ou usar o endpoint /auth/v1/admin/users para tracking

Status: ℹ️ MELHORIA RECOMENDADA


1.2 VALIDAÇÃO DE ENTRADA


FINDING 1.2.1 [ALTA]

Severidade: 🟠 ALTA Localização: supabase/functions/ti-webhook-receiver/index.ts:255-262 CWE: CWE-20 (Improper Input Validation)

Descrição: Event type não é validado contra lista de valores esperados.

Evidência:

// Linha 255-262
if (event_type !== "publications.created") {
  console.log(`[Webhook TI] Evento ignorado: '${event_type}'`);
  return new Response(
    JSON.stringify({ message: "Evento ignorado", event_type }),
    // ⚠️ Informa ao atacante quais tipos de eventos existem
  );
}

Recomendação:

  1. Usar validação com conjunto fechado (enum)
  2. Não informar detalhes sobre eventos existentes na resposta

Status: ℹ️ MELHORIA RECOMENDADA


FINDING 1.2.2 [BAIXA]

Severidade: 🔵 BAIXA Localização: supabase/functions/ti-webhook-receiver/index.ts:170-176 CWE: CWE-347 (Improper Verification of Cryptographic Signature)

Descrição: Mesmo secret usado para HMAC-SHA256 e Bearer token.

Evidência:

// Linha 170-176
const queryToken = url.searchParams.get("token");
if (queryToken) {
  if (timingSafeEqual(queryToken, secret)) {
    return { ok: true, method: "query-token" };
  }
}
// ⚠️ O mesmo secret valida HMAC e Bearer

Recomendação:

  1. Usar secrets diferentes para cada método de autenticação
  2. Preferir HMAC-SHA256 como único método

Status: ℹ️ MELHORIA RECOMENDADA


FINDING 1.2.3 [INFO]

Severidade: 🟢 INFO Localização: src/components/ui/chart.jsx:62-79 CWE: N/A

Descrição: Uso de dangerouslySetInnerHTML para CSS dinâmico.

Evidência:

// Linhas 62-79
return (
  <style
    dangerouslySetInnerHTML={{
      __html: Object.entries(THEMES)
        .map(/* ... */)
        .join("\n"),
    }}
  />
);

Avaliação: SEGURO - conteúdo é gerado programmaticamente, não inclui input do usuário.


FINDING 1.2.4 [INFO]

Severidade: 🟢 INFO Localização: Múltiplas Edge Functions CWE: N/A

Descrição: Boas práticas observadas - validação de entrada robusta.

Evidência:

  • chat-jurisprudencia/index.ts:397-413 - Validação estrita de tipos com verificações explícitas
  • generate-embedding/index.ts:91-122 - Limites de tamanho e validação de taskType
  • ai-proxy/index.ts:517-528 - Verificação de campos obrigatórios
  • datajud-bypass/index.ts:77-94 - Validação de campos obrigatórios e tipos

Recomendação: Manter este padrão.


1.3 GERENCIAMENTO DE SEGREDOS


FINDING 1.3.1 [INFO]

Severidade: 🟢 INFO Localização: Variáveis de ambiente CWE: N/A

Descrição: Segredos armazenados em variáveis de ambiente (Vault do Supabase).

Evidência:

const GEMINI_API_KEY = Deno.env.get("GEMINI_API_KEY");
const DATAJUD_API_KEY = Deno.env.get("DATAJUD_API_KEY");
const supabaseServiceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "";

Avaliação: CORRETO - Supabase Edge Functions usam Vault para secrets.

Recomendação:

  1. Garantir que todas as API keys estejam no Vault
  2. Nunca commitar .env files
  3. Rotacionar secrets periodicamente

RESUMO EXECUTIVO

SeveridadeQtdFindings
🔴 CRÍTICA4Auth bypass (delete-calendar, sync-calendar), JWT sem verificação
🟠 ALTA2Auth fraca (scrape-tnu), validação entrada insuficiente
🟡 MÉDIA2Bug de copia/cola, rate limiting ineficaz
🔵 BAIXA1Shared secrets
🟢 INFO3Boas práticas, CSS safe

AÇÕES PRIORITÁRIAS

  1. [IMEDIATO] Corrigir delete-google-calendar e sync-google-calendar - adicionar autenticação
  2. [IMEDIATO] Corrigir _shared/auth.ts - verificar assinatura HS256
  3. [URGENTE] Corrigir import-tramita-clients linha 267 - usar ANON_KEY correto
  4. [URGENTE] Corrigir scrape-tnu - usar autenticação completa

BOAS PRÁTICAS IDENTIFICADAS

✅ CORS restrito a domínios específicos em todas as Edge Functions
✅ Headers de segurança (X-Content-Type-Options, X-Frame-Options, HSTS)
✅ Autenticação via authenticateRequest implementada corretamente
✅ Rate limiting implementado (com ressalvas)
✅ Validação de entrada rigorosa na maioria das funções
✅ HMAC-SHA256 para webhooks
✅ Timeout em chamadas externas


Documento gerado automaticamente em 2026-04-18

Built with LogoFlowershow