Skip to content

fix(edge): restore schemas.ts after Lovable truncation#91

Merged
adm01-debug merged 1 commit into
mainfrom
chore/restore-schemas-shared
May 8, 2026
Merged

fix(edge): restore schemas.ts after Lovable truncation#91
adm01-debug merged 1 commit into
mainfrom
chore/restore-schemas-shared

Conversation

@adm01-debug
Copy link
Copy Markdown
Owner

@adm01-debug adm01-debug commented May 8, 2026

Contexto

O arquivo supabase/functions/_shared/schemas.ts, compartilhado por 27 edge functions consumidoras, foi truncado em 2026-05-06 pelo commit dbcdbe112 ("Changes") do Lovable.

Antes (estável) Depois (atual em main)
Linhas 329 35
Bytes 15.326 1.180
Schemas exportados 36+ 2
Helpers 4 (parseBody, UUIDSchema, EmailSchema, SafeStringSchema) 1 (parseBody)
Cobertura dos imports das edge functions 100% 7% (2/29 símbolos)

Resultado: 25 das 27 edge functions ficaram com import quebrado (símbolo ausente).

O que este PR faz

Restaura o conteúdo da última versão estável do arquivo: da8ecafc8 (2026-04-16).

Diff: +318 / -24 linhas (1 arquivo).

Cobertura validada (29/29 símbolos importados)

AiAutoTagSchema, AiChurnAnalysisSchema, AiClassifyTicketsSchema,
AiConversationAnalysisSchema, AiConversationSummarySchema,
AiEnhanceMessageSchema, AiSuggestReplySchema, ApprovePasswordResetSchema,
ChatbotL1Schema, ClassifyAudioMemeSchema, ClassifyEmojiSchema,
ClassifyStickerSchema, DetectNewDeviceSchema, ElevenLabsDialogueSchema,
ElevenLabsSFXSchema, ElevenLabsTTSSchema, ElevenLabsVoiceDesignCreateSchema,
ElevenLabsVoiceDesignPreviewSchema, ExternalDbBridgeSchema,
RateLimitAlertSchema, ScheduledReportSchema, SentimentAlertSchema,
SicoobBridgeMarkReadSchema, SicoobBridgeNewMessageSchema,
SicoobBridgeReplySchema, TranscribeAudioSchema, WebAuthnActionSchema,
parseBody, z

Edge functions afetadas (27)

ai-auto-tag, ai-churn-analysis, ai-classify-tickets, ai-conversation-analysis, ai-conversation-summary, ai-enhance-message, ai-proxy, ai-suggest-reply, ai-transcribe-audio, approve-password-reset, chatbot-l1, classify-audio-meme, classify-emoji, classify-sticker, detect-new-device, elevenlabs-dialogue, elevenlabs-sfx, elevenlabs-tts, elevenlabs-tts-stream, elevenlabs-voice-design, external-db-bridge, send-rate-limit-alert, send-scheduled-report, sentiment-alert, sicoob-bridge, sicoob-bridge-reply, webauthn.

Bonus: ajuste da URL do zod

A versão atual em main importa de https://deno.land/x/zod@v3.22.4/mod.ts (resíduo da truncação).
Outras edge functions do projeto usam https://esm.sh/zod@3.23.8.
A versão restaurada também usa esm.sh@3.23.8, alinhando o schemas com o resto do projeto.

Risco

🟢 Zero. Apenas restaura conteúdo conhecido (já rodou em produção de 16/abr a 06/mai).
Nenhum código consumidor precisa mudar — todos os imports voltam a resolver.

Plano de validação pós-merge

  1. Verificar que edge functions com schemas restaurados continuam respondendo (smoke test em prod)
  2. Caso CodeRabbit aponte algum schema desalinhado com o uso atual de uma edge function, abrir issue de follow-up

Source

  • Commit fonte: da8ecafc8 (16/abr/2026, "Changes")
  • Branch: chore/restore-schemas-shared
  • Commit deste PR: 79adbfb67

Summary by CodeRabbit

Tarefas Administrativas

  • Melhorado o sistema de validação e tratamento de erros internos para maior confiabilidade da plataforma.
  • Infraestrutura backend expandida para suportar novos serviços e fluxos de trabalho adicionais.

The schemas.ts shared by 27 edge functions was truncated by Lovable's
'Changes' commit (dbcdbe1) on 2026-05-06, going from 329 lines
(15.326 bytes, 36+ schemas) to 35 lines (1.180 bytes, 2 schemas).

This left 25 of 27 consuming edge functions with broken imports
(symbols missing). Restore the last known good version (da8ecaf
from 2026-04-16) which contains all 29 symbols imported by consumers.

URL fix: zod import goes from deno.land/x/zod@v3.22.4 (residual)
to esm.sh/zod@3.23.8 (consistent with other edge functions).

Source commit: da8ecaf (Apr 16, 2026)
Coverage: 29/29 imported symbols across 27 edge functions
Diff: +318 / -24 lines
Copilot AI review requested due to automatic review settings May 8, 2026 17:49
@vercel
Copy link
Copy Markdown

vercel Bot commented May 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
zapp-web Ready Ready Preview, Comment May 8, 2026 5:49pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack

Walkthrough

Expansão de um arquivo mínimo de schemas Zod em biblioteca compartilhada com 30+ schemas novos. Atualiza dependência Zod, re-exporta z, introduz base schemas reutilizáveis, refatora parseBody com retorno discriminado e error flattening, cobrindo IA, integrações externas, alertas e autenticação.

Changes

Expansão Centralizada de Schemas de Validação

Layer / File(s) Summary
Importação de Dependência e Schemas Base
supabase/functions/_shared/schemas.ts
Zod atualizado para v3.23.8 via esm.sh. Re-exporta z. Introduz UUIDSchema, EmailSchema, SafeStringSchema como base reutilizável.
MessageSchema e Schemas IA Principais
supabase/functions/_shared/schemas.ts
Define MessageSchema (role, content, sender, timestamp) como estrutura central. Redefine AiSuggestReplySchema com messages array (max 50) e contactName default. Adiciona AiEnhanceMessageSchema, AiConversationAnalysisSchema, AiAutoTagSchema, AiChurnAnalysisSchema, AiClassifyTicketsSchema.
Schemas para Serviços Externos
supabase/functions/_shared/schemas.ts
ElevenLabs TTS/SFX/diálogo/voice design (preview + create), transcrição com opções linguagem/áudio, classificadores (meme, emoji, sticker).
Email, Alertas e Reset de Senha
supabase/functions/_shared/schemas.ts
SendEmailSchema com validação de recipient/subject/body. SentimentAlertSchema e RateLimitAlertSchema para monitoramento. ApprovePasswordResetSchema. Redefine AiConversationSummarySchema com messages array (min/max constraints).
Integrações Básicas
supabase/functions/_shared/schemas.ts
ChatbotL1Schema, DetectNewDeviceSchema, ScheduledReportSchema. Trio Sicoob: SicoobBridgeNewMessageSchema, SicoobBridgeMarkReadSchema, SicoobBridgeReplySchema.
Autenticação e Ações API
supabase/functions/_shared/schemas.ts
GmailSendActionSchema (send), GmailOAuthActionSchema (OAuth flow), WebAuthnActionSchema (register/authenticate).
Bridge de BD e Voice
supabase/functions/_shared/schemas.ts
ExternalDbBridgeSchema, ExternalDbProxySchema. TalkXSendSchema, VoiceCopilotActionSchema. EvolutionApiSchema com allowlist de passthrough.
Refatoração parseBody
supabase/functions/_shared/schemas.ts
Parâmetro muda de body para data. Retorno explícito: { success: true; data: T } | { success: false; error: string }. Error formatting via error.flatten() com agregação form/field em lugar de .issues manual.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutos


Pontos Críticos para Revisão

🔴 Segurança & Validação:

  • Verificar se parseBody está sendo chamado em todas as Edge Functions que usarão os novos schemas. Checar se há any sem narrowing após validação.
  • ExternalDbBridgeSchema e ExternalDbProxySchema: garantir que operações no BD externo não aceitam queries sem validação adicional (SQL injection).
  • EvolutionApiSchema com allowlist de passthrough: confirmar que a allowlist não permite payloads destrutivos sem autenticação/autorização.

🟡 Integração:

  • GmailOAuthActionSchema com OAuth flow: verificar se tokens sensíveis NÃO estão sendo logados em erro messages (evitar secrets expostas em error: string).
  • parseBody refatorado: garantir que callers antigos usando .issues diretamente foram atualizados para o novo formato de retorno.

🟢 Performance:

  • MessageSchema com array max 50: confirmado que conversas longas serão paginadas ou truncadas upstream.
  • Schemas com z.array() sem limite explícito (ex: ClassifyEmojiSchema, ClassifyStickerSchema): adicionar maxLength se processam grandes lotes.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed O título descreve com precisão a mudança principal: restaurar um arquivo truncado acidentalmente no Lovable. Deixa claro o tipo (fix), escopo (edge) e ação (restore).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/restore-schemas-shared

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 79adbfb676

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


// ─── Chatbot L1 ──────────────────────────────────────────────
export const ChatbotL1Schema = z.object({
contactId: z.string().uuid("contactId must be a valid UUID"),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow chatbot health-check payloads through validation

This validator now requires contactId to be a UUID, but the existing settings health check invokes chatbot-l1 with body: { contactId: 'test', message: 'Olá, teste de conexão', connectionId: 'test' } in src/components/settings/ChatbotL1Config.tsx:234-236. In that UI path the request will be rejected by parseBody before the function can report connectivity, so the “Testar Conexão” button always shows an error even when the edge function is reachable.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR restores the previously-truncated shared Edge Functions validation module (supabase/functions/_shared/schemas.ts), reintroducing the full set of Zod schemas and helpers consumed across many Supabase Edge Functions, and aligns the Zod import URL with the rest of the project.

Changes:

  • Restores the complete set of shared Zod schemas (AI, ElevenLabs, alerts, bridges, etc.) and helper exports that were removed by truncation.
  • Re-exports z and reinstates common helpers (UUIDSchema, EmailSchema, SafeStringSchema, parseBody) for consistent input validation across functions.
  • Switches Zod import back to https://esm.sh/zod@3.23.8 to match existing project usage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 22 to +26
export const AiSuggestReplySchema = z.object({
messages: z.array(MessageSchema).max(50).optional(),
contactName: z.string().max(200).optional().default('Cliente'),
contactId: z.string().uuid().optional().nullable(),
context: z.string().max(500).optional(),
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@supabase/functions/_shared/schemas.ts`:
- Around line 256-264: A schema atual aceita actions sem exigir os campos
mínimos por operação; refatore ExternalDbBridgeSchema para usar uma união
discriminada baseada em action (por exemplo via z.discriminatedUnion or z.union)
e criar variantes específicas para 'select' (requere table plus opcional
filters/limit/offset/countMode), 'rpc' (requere rpc e params), 'insert' (requere
table e params/data), 'update' (requere table e params e/ou match) e 'delete'
(requere table e match), garantindo que cada variant valide os campos
necessários (table, rpc, params, match, etc.); aplique a mesma mudança para
ExternalDbProxySchema (as variantes nas linhas 295-315) para evitar validação
permissiva que deixa erros aparecerem só em runtime ou permite operações
destrutivas sem campos obrigatórios.
- Around line 93-99: The audioUrl field in TranscribeAudioSchema currently only
checks URL format (z.string().url()) and must be restricted to HTTPS and
optionally to an allowlist of domains; update the audioUrl validator on
TranscribeAudioSchema (audioUrl) to first validate URL format, then .refine(...)
to require it starts with "https://" (or check new URL(value).protocol ===
"https:"), and optionally add a hostname allowlist check (parse new
URL(value).hostname and assert it is in the allowedDomains array) so internal
hosts are rejected; apply the same change to the other URL fields mentioned (the
schemas around lines 102-114) using the same pattern and clear error messages.
- Around line 15-20: O MessageSchema está permitindo objetos vazios (por exemplo
{}), o que faz com que entradas sem conteúdo ainda satisfaçam os .min(5) em
AiConversationAnalysisSchema e AiConversationSummarySchema; corrija
MessageSchema tornando o campo content obrigatório e proibindo string vazia (por
exemplo trocar content: z.string().max(5000).optional() por uma string não-vazia
com min(1) e max(5000)), mantendo outros campos opcionais conforme necessário,
para que mensagens sem texto não sejam aceitas.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6e544eee-77e1-4875-b21f-d9ddd436c943

📥 Commits

Reviewing files that changed from the base of the PR and between a21203f and 79adbfb.

📒 Files selected for processing (1)
  • supabase/functions/_shared/schemas.ts

Comment on lines +15 to 20
export const MessageSchema = z.object({
sender: z.string().max(50).optional(),
content: z.string().max(5000).optional(),
created_at: z.string().optional(),
message_type: z.string().max(50).optional(),
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

MessageSchema está aceitando mensagens vazias.

Do jeito que está, {} passa e ainda conta para o .min(5) de AiConversationAnalysisSchema e AiConversationSummarySchema. Isso deixa payload sem conteúdo entrar nas rotas de IA e enfraquece a validação no ponto mais compartilhado.

Diff sugerido
 export const MessageSchema = z.object({
   sender: z.string().max(50).optional(),
   content: z.string().max(5000).optional(),
   created_at: z.string().optional(),
   message_type: z.string().max(50).optional(),
-});
+}).refine(
+  (message) => Boolean(message.content?.trim() || message.message_type),
+  { message: "Message must include content or message_type" },
+);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@supabase/functions/_shared/schemas.ts` around lines 15 - 20, O MessageSchema
está permitindo objetos vazios (por exemplo {}), o que faz com que entradas sem
conteúdo ainda satisfaçam os .min(5) em AiConversationAnalysisSchema e
AiConversationSummarySchema; corrija MessageSchema tornando o campo content
obrigatório e proibindo string vazia (por exemplo trocar content:
z.string().max(5000).optional() por uma string não-vazia com min(1) e
max(5000)), mantendo outros campos opcionais conforme necessário, para que
mensagens sem texto não sejam aceitas.

Comment on lines +93 to +99
export const TranscribeAudioSchema = z.object({
audioUrl: z.string().url("Invalid audio URL").max(2048),
messageId: z.string().max(100).optional(),
languageCode: z.enum(ALLOWED_LANGUAGES).optional().default('por'),
enableDiarization: z.boolean().optional().default(false),
tagAudioEvents: z.boolean().optional().default(true),
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

As URLs remotas precisam ser restringidas.

z.string().url() só valida formato. Para functions que baixam áudio/imagem, isso permite apontar para hosts internos ou metadata se o handler fizer fetch() direto nesses campos. Pelo menos force https:; idealmente use allowlist dos domínios esperados.

Diff sugerido
+const RemoteAssetUrlSchema = z.string().url().max(2048).refine((value) => {
+  const url = new URL(value);
+  return url.protocol === "https:";
+}, "Only HTTPS URLs are allowed");
+
 export const TranscribeAudioSchema = z.object({
-  audioUrl: z.string().url("Invalid audio URL").max(2048),
+  audioUrl: RemoteAssetUrlSchema,
   messageId: z.string().max(100).optional(),
   languageCode: z.enum(ALLOWED_LANGUAGES).optional().default('por'),
   enableDiarization: z.boolean().optional().default(false),
   tagAudioEvents: z.boolean().optional().default(true),
 });
 
 export const ClassifyAudioMemeSchema = z.object({
-  audio_url: z.string().url().max(2048).optional().nullable(),
+  audio_url: RemoteAssetUrlSchema.optional().nullable(),
   file_name: z.string().max(500).optional().nullable(),
 });
 
 export const ClassifyEmojiSchema = z.object({
-  image_url: z.string().url().max(2048).optional().nullable(),
+  image_url: RemoteAssetUrlSchema.optional().nullable(),
   file_name: z.string().max(500).optional().nullable(),
 });
 
 export const ClassifyStickerSchema = z.object({
-  image_url: z.string().url().max(2048).optional().nullable(),
+  image_url: RemoteAssetUrlSchema.optional().nullable(),
 });

Also applies to: 102-114

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@supabase/functions/_shared/schemas.ts` around lines 93 - 99, The audioUrl
field in TranscribeAudioSchema currently only checks URL format
(z.string().url()) and must be restricted to HTTPS and optionally to an
allowlist of domains; update the audioUrl validator on TranscribeAudioSchema
(audioUrl) to first validate URL format, then .refine(...) to require it starts
with "https://" (or check new URL(value).protocol === "https:"), and optionally
add a hostname allowlist check (parse new URL(value).hostname and assert it is
in the allowedDomains array) so internal hosts are rejected; apply the same
change to the other URL fields mentioned (the schemas around lines 102-114)
using the same pattern and clear error messages.

Comment on lines +256 to +264
export const ExternalDbBridgeSchema = z.object({
action: z.enum(['select', 'rpc', 'insert', 'update', 'delete']),
table: z.string().max(100).optional(),
rpc: z.string().max(100).optional(),
params: z.record(z.unknown()).optional(),
limit: z.number().int().min(1).max(1000).optional(),
offset: z.number().int().min(0).optional(),
countMode: z.string().max(20).optional(),
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

As operações de DB externa não estão validadas por action.

Hoje payloads como { action: "delete" }, { action: "rpc" } ou { action: "update", table: "x" } passam na schema mesmo sem os campos mínimos da operação. Em proxy/bridge de banco isso troca erro de validação por erro em runtime — e, no caso destrutivo, deixa a segurança depender de defaults do handler.

Direção de ajuste
-export const ExternalDbBridgeSchema = z.object({
-  action: z.enum(['select', 'rpc', 'insert', 'update', 'delete']),
-  table: z.string().max(100).optional(),
-  rpc: z.string().max(100).optional(),
-  params: z.record(z.unknown()).optional(),
-  limit: z.number().int().min(1).max(1000).optional(),
-  offset: z.number().int().min(0).optional(),
-  countMode: z.string().max(20).optional(),
-});
+export const ExternalDbBridgeSchema = z.discriminatedUnion('action', [
+  z.object({
+    action: z.literal('select'),
+    table: z.string().max(100),
+    limit: z.number().int().min(1).max(1000).optional(),
+    offset: z.number().int().min(0).optional(),
+    countMode: z.string().max(20).optional(),
+  }),
+  z.object({
+    action: z.literal('rpc'),
+    rpc: z.string().max(100),
+    params: z.record(z.unknown()).optional(),
+  }),
+  z.object({
+    action: z.literal('insert'),
+    table: z.string().max(100),
+    params: z.record(z.unknown()),
+  }),
+  z.object({
+    action: z.literal('update'),
+    table: z.string().max(100),
+    params: z.record(z.unknown()),
+  }),
+  z.object({
+    action: z.literal('delete'),
+    table: z.string().max(100),
+    // exigir critério/filtro aqui
+  }),
+]);

A mesma ideia vale para ExternalDbProxySchema: tornar action obrigatório e exigir table/rpc/data/match|filters conforme cada variante.

Also applies to: 295-315

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@supabase/functions/_shared/schemas.ts` around lines 256 - 264, A schema atual
aceita actions sem exigir os campos mínimos por operação; refatore
ExternalDbBridgeSchema para usar uma união discriminada baseada em action (por
exemplo via z.discriminatedUnion or z.union) e criar variantes específicas para
'select' (requere table plus opcional filters/limit/offset/countMode), 'rpc'
(requere rpc e params), 'insert' (requere table e params/data), 'update'
(requere table e params e/ou match) e 'delete' (requere table e match),
garantindo que cada variant valide os campos necessários (table, rpc, params,
match, etc.); aplique a mesma mudança para ExternalDbProxySchema (as variantes
nas linhas 295-315) para evitar validação permissiva que deixa erros aparecerem
só em runtime ou permite operações destrutivas sem campos obrigatórios.

export const SafeStringSchema = (maxLen = 10000) => z.string().max(maxLen).transform(s => s.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '').trim());

// ─── AI function schemas ─────────────────────────────────────
export const MessageSchema = z.object({
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MessageSchema makes 'content' and 'sender' optional when they should be required, and 'sender' lacks enum validation

Fix on Vercel

@adm01-debug
Copy link
Copy Markdown
Owner Author

Decisão de merge

Status dos checks

CodeRabbit Pro — 3 findings 🟠 Major (não bloqueantes)

Os 3 findings já estavam presentes na versão estável da8ecafc8 (16/abr/2026) que rodou em produção sem incidente. Eles representam hardenings pré-existentes, não regressões introduzidas por este PR:

  1. MessageSchema aceita {} vazio → cascata pra AiConversationAnalysisSchema e AiConversationSummarySchema (linha 20)
  2. URLs remotas usam z.string().url() sem forçar https:// ou bloquear hosts internos → risco SSRF em TranscribeAudioSchema, ClassifyAudioMemeSchema, ClassifyEmojiSchema, ClassifyStickerSchema (linha 99)
  3. ExternalDbBridgeSchema e ExternalDbProxySchema aceitam { action: "delete" } sem campos obrigatórios mínimos por operação (linha 264)

Ação

Mergeio agora — escopo deste PR é apenas restaurar o estado funcional das 27 edge functions (de 7% para 100% de cobertura de imports). Os 3 hardenings ficam como backlog dedicado: PR-FUTURO-E (Schemas Hardening).

@adm01-debug adm01-debug merged commit 90b09c7 into main May 8, 2026
14 of 15 checks passed
@adm01-debug adm01-debug deleted the chore/restore-schemas-shared branch May 8, 2026 17:55
adm01-debug added a commit that referenced this pull request May 8, 2026
Atende finding 🟠 Major do CodeRabbit no PR #91:
MessageSchema aceitava {} vazio, deixando entrar payload sem
conteúdo nas rotas de IA via cascata em AiConversationAnalysisSchema
e AiConversationSummarySchema.

Mudança:
- sender: agora obrigatório, .min(1) (não-vazio)
- content: agora obrigatório (campo presente), .max(5000) mantido,
  mas SEM .min(1) — content vazio é legítimo em produção
  (16.9% das mensagens reais: imagens/áudios/documentos sem caption)

Validado em 23 cenários (stress test exaustivo) + cruzamento com
672.003 mensagens reais do banco.

Refs: PR #91 review comment r3210444961
Source: PR-FUTURO-E1 em /workspace/notes/pr-futuro-e-schemas-hardening.md
adm01-debug added a commit that referenced this pull request May 8, 2026
* harden(edge): RemoteUrlSchema anti-SSRF (E2)

Atende finding 🟠 Major do CodeRabbit no PR #91:
URLs remotas usavam apenas z.string().url() que valida formato mas
permite hosts internos, criando risco SSRF nas edge functions que
fazem fetch direto (ai-transcribe-audio linha 46) ou que enviam URL
para serviços externos que fazem fetch (classify-audio-meme/emoji/
sticker -> Lovable AI Gateway / OpenAI Vision).

Mudanças:
- Novo helper RemoteUrlSchema com proteções:
  * HTTPS-only (rejeita http/ftp/data/javascript/file)
  * Bloqueia IPv4 privados (10/8, 127/8, 169.254/16, 172.16/12, 192.168/16)
  * Bloqueia IPv6 loopback/link-local/ULA (::1, fe80::/10, fc00::/7)
  * Bloqueia hostnames reservados (localhost, *.local, *.internal)
  * Max 2048 chars
- Aplicado em 4 schemas:
  * TranscribeAudioSchema.audioUrl
  * ClassifyAudioMemeSchema.audio_url (preserva .optional().nullable())
  * ClassifyEmojiSchema.image_url
  * ClassifyStickerSchema.image_url

Validado em 41 cenários de stress-test cobrindo Supabase Storage,
CDNs, S3, Cloudfront, AWS/GCP/DigitalOcean metadata endpoints, IPv6,
Punycode, casos exóticos (user@host bypass, FTP, javascript:, data:).

Refs: PR #91 review comment r3210444970
Source: PR-FUTURO-E2 em /workspace/notes/pr-futuro-e-schemas-hardening.md

* fix(edge): RemoteUrlSchema regex IPv6 não bloqueia FQDNs (E2 fixup)

CodeRabbit Pro identificou 2 bugs no commit anterior:

1. host.startsWith('fc') / startsWith('fd') bloqueava FQDNs legítimos
   começando com fc/fd (ex: fcdn.com, fdic.gov, fc-paris.fr).
   Fix: aplicar regra IPv6 só quando host contém ':' (literal IPv6).

2. host.startsWith('fe80:') cobria apenas /16 (fe80..fe8f), perdendo
   o range completo /10 do link-local (fe80..febf).
   Fix: regex /^fe[89ab][0-9a-f]?:/ cobre fe80..febf.

Adicional: documentado que schema validation NÃO previne DNS rebinding
nem redirects pós-fetch. Defesa completa requer DNS re-check, controle
de redirect, ou outbound proxy — tracked como follow-up arquitetural.

Validado em 23 cenários adicionais cobrindo FQDNs com prefixos fc/fd/fe
e todo o range link-local fe80::/10.

Refs: PR #93 review comments coderabbit no chore/schemas-hardening-e2
adm01-debug added a commit that referenced this pull request May 8, 2026
…3) (#94)

Atende finding 🟠 Major do CodeRabbit no PR #91:
ExternalDbBridgeSchema aceitava payloads incompletos como
{ action: 'delete' } sem table/match — schema permitia DELETE sem
condição WHERE chegar no runtime, dependendo apenas da validação
manual em external-db-bridge/index.ts:146.

Mudanças (defense in depth — shape preservada para compatibilidade):
- 6 refines per-action no schema:
  * SELECT/INSERT/UPDATE/DELETE: table não-vazia obrigatória
  * RPC: rpc name não-vazio obrigatório
  * DELETE: params.match não-vazio obrigatório (proteção crítica
    contra DELETE table-wide acidental)
  * UPDATE: params.match não-vazio + params.values obrigatórios
    (proteção crítica contra UPDATE table-wide)
  * INSERT: params com rows[] ou campos diretos obrigatório

Decisão de design — refines em vez de discriminatedUnion:
discriminatedUnion mudaria o shape do schema e poderia quebrar
callers desconhecidos. Refines mantêm shape compatível, apenas
adicionando validação. Banco mostra 0 chamadas reais ao bridge
nos últimos 30 dias (telemetria 'web_vital' vai direto via
client-observability, não via bridge), tornando o risco de
quebrar caller existente praticamente nulo.

Validado em 34 cenários de stress-test cobrindo:
- DELETE sem match (5 variantes: undefined, null, {}, string, array)
- UPDATE sem match/values (6 variantes)
- INSERT sem params/rows/fields (4 variantes)
- Casos válidos (SELECT, RPC, INSERT, UPDATE, DELETE com payload bom)
- Casos absurdos (action inválida, body vazio, null direto)

Refs: PR #91 review comment r3210444976
Source: PR-FUTURO-E3 em /workspace/notes/pr-futuro-e-schemas-hardening.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants