🔒 AUDITORIA DE SEGURANÇA - VALIDAÇÃO DE ENTRADA

Resumo Executivo

Status: ⚠️ CRÍTICO - Múltiplas vulnerabilidades de alta severidade detectadas

A arquitetura de validação do projeto apresenta desconexão crítica entre schemas Zod definidos e sua aplicação prática. Os schemas são robustos, mas NÃO estão sendo utilizados na camada de serviço, resultando em validação apenas no cliente (bypassável).


1.2.1 SCHEMAS ZOD

📋 Lista Completa de Schemas Definidos

SchemaCampos ValidadosObrigatóriosDefesa DoSStatus Uso
clientCreateSchema11nome, cpf_cnpj✅ max 200NÃO USADO
appointmentCreateSchema10client_id, title, date✅ max 200NÃO USADO
processCreateSchema12client_id, numero, tipo✅ max 200/2000NÃO USADO
deadlineCreateSchema7titulo, due_date✅ max 1000NÃO USADO
taskCreateSchema8titulo✅ max 200/2000NÃO USADO
documentUploadSchema6fileName, fileSize, mimeType✅ max 255NÃO USADO

🔴 FINDING-001: Código Morto - Schemas Não Utilizados

Severidade: CRÍTICA 🔴
Localização: src/services/clientService.js:10-13, src/services/baseService.js:127-129
CWE: CWE-20 (Improper Input Validation), CWE-807 (Reliance on Untrusted Inputs)

Descrição:
Todos os schemas Zod estão definidos mas NENHUM está sendo injetado nos serviços. O ClientService foi explicitamente configurado SEM schema:

Evidência:

// src/services/clientService.js:10-14
// NOTA: Schemas Zod de cliente removidos da camada de serviço.
// O clientCreateSchema usa 'nome' mas a tabela 'clients' usa 'full_name'.
// A validação de campos obrigatórios é feita pela constraint NOT NULL do banco.
class ClientService extends BaseService {
  constructor() {
    super("clients"); // Schema = null!
  }
}
// src/services/baseService.js:190-206
async create(recordData) {
  // Validar com Zod se schema existir
  const validatedData = this.schema          // ← sempre null
    ? validateWithZod(recordData, this.schema, 'criar')
    : recordData;                            // ← dados brutos passam!
  
  const { data, error } = await supabase
    .from(this.table)
    .insert(payload)  // ← injeção direta no DB
    .select()
    .single();
}

Recomendação:

  1. Criar schema específico para tabela clients (campos: full_name, cpf_cnpj)
  2. Injetar schema em TODOS os serviços:
import { clientCreateSchema } from '@/lib/validation/schemas';
class ClientService extends BaseService {
  constructor() {
    super("clients", clientCreateSchema);
  }
}

🟡 FINDING-002: Discrepância Schema vs Tabela

Severidade: ALTA 🟡
Localização: src/lib/validation/schemas/index.ts:105-127 vs Tabela clients
CWE: CWE-20 (Improper Input Validation)

Descrição:
O clientCreateSchema usa nome mas a tabela usa full_name. O schema endereco usa nomes em português (logradouro, numero) enquanto a tabela provavelmente usa inglês.

Evidência:

// Schema define:
nome: z.string()              // ❌ Tabela espera: full_name
endereco: {
  logradouro: z.string(),    // ❌ Provavelmente espera: street
  numero: z.string(),        // ❌ Provavelmente espera: number
}

Recomendação:
Sincronizar schema com DDL da tabela ou criar mapper de campos.


1.2.2 VALIDAÇÃO DE CPF/CNPJ

📊 Análise do Algoritmo

AspectoImplementaçãoStatus
Verificação de dígitos repetidos/^(\d)\1{10}$/ (CPF), /^(\d)\1{13}$/ (CNPJ)✅ Correta
Sanitização de máscarareplace(/[^\d]/g, "")✅ Correta
Comprimento exatocpf.length !== 11 / cnpj.length !== 14✅ Correta
Cálculo de dígitos verificadores✅ Implementado✅ Correta
CPFs válidos de teste✅ 529.982.247-25, 111.444.777-35✅ Passam
CPFs inválidos rejeitados✅ 111.111.111-11, 000.000.000-00✅ Rejeitados

🟢 FINDING-003: Validação CPF/CNPJ Robusta (Positivo)

Severidade: INFO 🟢
Localização: src/lib/validation/schemas/index.ts:25-40, src/lib/validation/schemas/index.ts:46-73
CWE: N/A (Boas práticas)

Evidência de Testes:

// CPF inválido - dígitos repetidos → REJEITADO ✅
validarCPF("111.111.111-11") // false
validarCPF("000.000.000-00") // false

// CPF válido → ACEITO ✅
validarCPF("529.982.247-25") // true
validarCPF("111.444.777-35") // true

// CNPJ inválido - dígitos repetidos → REJEITADO ✅
validarCNPJ("11.111.111/1111-11") // false

// CNPJ válido → ACEITO ✅
validarCNPJ("11.444.777/0001-61") // true

🟠 FINDING-004: Regex Inadequado para Caracteres Permitidos

Severidade: MÉDIA 🟠
Localização: src/lib/validation/schemas/index.ts:81
CWE: CWE-347 (Improper Verification of Cryptographic Signature)

Descrição:
O regex /^[\d.\-/]+$/ permite CPFs com formato malformado:

Evidência:

// Aceita formatos inválidos:
"52998224725....."  // ✅ PASSA - only digits and dots allowed
"529.982.247-25--"  // ✅ PASSA - hífen no fim
"...---52998224725" // ✅ PASSA - pontos/hífens no início

// Não verifica estrutura de máscara correta

Recomendação:

// Opção A: Regex estrita de formato
const cpfCnpjSchema = z.string()
  .regex(/^\d{3}\.\d{3}\.\d{3}-\d{2}$|\d{11}$|^\d{2}\.\d{3}\.\d{3}\/\d{4}-\d{2}$|\d{14}$/, 
    "Formato de CPF/CNPJ inválido")
  .refine((val) => {
    const clean = val.replace(/[^\d]/g, "");
    if (clean.length === 11) return validarCPF(clean);
    if (clean.length === 14) return validarCNPJ(clean);
    return false;
  }, "CPF ou CNPJ inválido");

🔴 FINDING-005: ClientForm.jsx NÃO USA Schema Zod

Severidade: CRÍTICA 🔴
Localização: src/components/clients/ClientForm.jsx:67-195
CWE: CWE-20 (Improper Input Validation)

Descrição:
O form de cliente faz validação manual via HTML5 required e maxLength, ignorando completamente o clientCreateSchema.

Evidência:

// ClientForm.jsx:282-291
<Input
  id="cpf_cnpj"
  value={formData.cpf_cnpj}
  onChange={handleCPFChange}
  required                    // HTML5 validation only
  maxLength={14}              // Only length check
  placeholder="000.000.000-00"
/>

// SEM validação de regex
// SEM validação de algoritmo matemático
// SEM validação Zod
// ClientForm.jsx:154-195 - handleSubmit NÃO valida com Zod
const handleSubmit = (e) => {
  e.preventDefault();
  // Saneamento básico apenas - sem validação estrutural
  const payload = { ...formData };
  delete payload.id;
  // ... mais deleções
  onSave(payload);  // ← Envia direto sem validação!
}

Recomendação:

import { clientCreateSchema } from '@/lib/validation/schemas';
import { z } from 'zod';

const handleSubmit = (e) => {
  e.preventDefault();
  
  const result = clientCreateSchema.safeParse(formData);
  if (!result.success) {
    toast.error("Dados inválidos: " + result.error.issues.map(i => i.message).join(", "));
    return;
  }
  
  onSave(result.data);
}

🟡 FINDING-006: Duplicação de Código de Validação

Severidade: ALTA 🟡
Localização: src/lib/validation/schemas/index.ts:25-73 vs src/components/documents/OCRExtractor.jsx:12-62
CWE: CWE-1041 (Use of Redundant Code)

Descrição:
Validação CPF/CNPJ existe em 2 locais diferentes, com implementações ligeiramente diferentes (OCR não verifica dígitos repetidos em CNPJ!).

Evidência:

// OCRExtractor.jsx:32-34 (VERSÃO INCOMPLETA!)
function validarCNPJ(cnpj) {
  cnpj = cnpj.replace(/[^\d]/g, "");
  if (cnpj.length !== 14) return false;
  // ❌ FALTA verificação de dígitos repetidos!
  // if (/^(\d)\1{13}$/.test(cnpj)) return false; ← NÃO EXISTE
}
// schemas/index.ts:48 (VERSÃO CORRETA)
if (cnpj.length !== 14 || /^(\d)\1{13}$/.test(cnpj)) return false;
// ✅ Verificação de dígitos repetidos presente

Recomendação:
Exportar funções de schemas/index.ts e reutilizar em todo o projeto:

import { validarCPF, validarCNPJ } from '@/lib/validation/schemas';

1.2.3 SECURITY SCHEMAS

📊 Configurações Definidas

ConstanteValorStatus
MAX_PAYLOAD_SIZE10MB (10 * 1024 * 1024)✅ Adequado
MAX_UPLOAD_SIZE50MB (50 * 1024 * 1024)✅ Adequado

🟢 FINDING-007: Lista de MIME Types Segura

Severidade: INFO 🟢
Localização: src/lib/validation/security-schemas.js:9-16
CWE: N/A (Boas práticas)

ALLOWED_MIME_TYPES = [
  "application/pdf",                                        // ✅ OK
  "image/jpeg", "image/png", "image/webp",                  // ✅ OK
  "application/msword",                                     // ✅ OK (doc)
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document", // ✅ OK (docx)
]

NENHUM mime type perigoso - Não inclui:

  • text/html (XSS)
  • application/javascript (XSS)
  • text/xml (XXE)
  • Executáveis (application/exe, etc.)

🟠 FINDING-008: documentUploadSchema Não Usada

Severidade: MÉDIA 🟠
Localização: src/lib/validation/schemas/index.ts:347-365
CWE: CWE-434 (Unrestricted Upload of File with Dangerous Type)

Descrição:
O schema documentUploadSchema valida MIME type via enum, mas não é usado em nenhum serviço de upload.

Evidência:

// Schema existe:
export const documentUploadSchema = z.object({
  mimeType: z.enum([...ALLOWED_MIME_TYPES]), // ✅ Restrição forte
});

// Mas uploadService NÃO existe no grep - schemas não aplicados

Recomendação:
Criar DocumentService com schema aplicado ou validar no edge function de upload.


1.2.4 RESUMO EXECUTIVO

Severidade das Vulnerabilidades

SeveridadeContagemIssues
🔴 CRÍTICA2FINDING-001, FINDING-005
🟡 ALTA2FINDING-002, FINDING-006
🟠 MÉDIA2FINDING-004, FINDING-008
🟢 INFO2FINDING-003, FINDING-007

Vulnerabilidades Remotas Exploráveis

  1. Bypass de validação CPF → CPFs matematicamente inválidos aceitos
  2. Payload DoS → Strings sem limite passam para DB (via campos sem .max())
  3. Injeção (potencial) → Sem validação Zod em service layer

Ações Prioritárias

  1. URGENTE: Injetar schemas em todos os serviços (ClientService, etc.)
  2. URGENTE: Atualizar ClientForm.jsx para usar clientCreateSchema.parse()
  3. IMPORTANTE: Consolidar funções validarCPF/CNPJ em único local
  4. IMPORTANTE: Corrigir regex de máscara em cpfCnpjSchema
  5. MÉDIO: Criar schema de cliente alinhado com nome da tabela (full_name)
  6. MÉDIO: Implementar MAX_PAYLOAD_SIZE middleware no edge

Relatório gerado em: 2026-04-18
Auditor: OpenShell Security Analysis
Scope: src/lib/validation/, src/services/, src/components/clients/

Built with LogoFlowershow