Skip to content

harden(edge): MessageSchema sender/content obrigatórios (E1)#92

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

harden(edge): MessageSchema sender/content obrigatórios (E1)#92
adm01-debug merged 1 commit into
mainfrom
chore/schemas-hardening-e1

Conversation

@adm01-debug
Copy link
Copy Markdown
Owner

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

Contexto

Atende finding 🟠 Major do CodeRabbit no PR #91 (comment r3210444961):

MessageSchema está aceitando mensagens vazias. Do jeito que está, {} passa e ainda conta para o .min(5) de AiConversationAnalysisSchema e AiConversationSummarySchema.

Mudança

Campo Antes Depois
sender z.string().max(50).optional() z.string().min(1, "sender is required").max(50)
content z.string().max(5000).optional() z.string().max(5000) (obrigatório existir, mas pode ser vazio)
created_at z.string().optional() mantém
message_type z.string().max(50).optional() mantém

Diff: +2 / -2 linhas, 1 arquivo.

Por que content NÃO recebeu .min(1) (revisão importante do plano original)

Investigação no banco real (672.003 mensagens em 90 dias) mostrou que content vazio é legítimo em 16.9% das mensagens (113.677 ocorrências):

message_type Count vazio Por que
image 10.257 Imagem sem caption
audio 3.912 Áudio (transcrição em fluxo separado)
document 1.497 Arquivo sem texto
video 1.033 Vídeo sem caption
sticker 552 Figurinha
text 1.558 Placeholder/edge cases
outros ~94k reactions, listMessage, etc

Forçar content.min(1) quebraria o frontend ao montar arrays de mensagens para ai-conversation-summary / ai-conversation-analysis quando incluísse imagens/áudios sem caption.

Validação

23 cenários de stress-test — todos com comportamento esperado.

Banco real: sender vazio = 0 ocorrências em 30 dias.

Callers frontend (verificados):

  • AISuggestions.tsx → ai-suggest-reply: {content, sender}
  • ConversationSummary.tsx → ai-conversation-summary: {sender, content, created_at}
  • AIConversationAssistant.tsx → ai-conversation-analysis: {id, sender, content, type, created_at}
  • AIAutoTagsConfig.tsx → ai-auto-tag: não envia messages (optional no schema pai)

Risco

🟢 Zero. Rejeita só o que era inválido ({} vazio). Aceita 100% das 672k mensagens reais validadas.

Referências

Summary by CodeRabbit

Notas de Versão

  • Correções de Bug
    • Aprimoramento na validação de mensagens: o campo de remetente agora é obrigatório com um valor válido, e o conteúdo da mensagem não pode estar vazio. Essas alterações garantem que todas as mensagens tenham informações completas e válidas antes de serem processadas.

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
Copilot AI review requested due to automatic review settings May 8, 2026 18:10
@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 6:10pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack

Walkthrough

MessageSchema no módulo de schemas compartilhado torna sender e content campos obrigatórios em vez de opcionais, adicionando validação de string não-vazia para sender com mensagem de erro explícita.

Changes

Validação de Campos Obrigatórios

Layer / File(s) Resumo
Schema de Campos Obrigatórios
supabase/functions/_shared/schemas.ts
sender muda de z.string().max(50).optional() para z.string().min(1, "sender is required").max(50); content muda de z.string().max(5000).optional() para z.string().max(5000), tornando ambos obrigatórios.

Estimated code review effort

🎯 2 (Simples) | ⏱️ ~3 minutos

Possibly related PRs

  • adm01-debug/zapp-web#91: Também modifica MessageSchema em supabase/functions/_shared/schemas.ts, alterando restrições do mesmo schema.
🚥 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: enrijecimento do MessageSchema tornando sender/content obrigatórios, corrigindo um finding crítico do CodeRabbit.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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/schemas-hardening-e1

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

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 hardens the shared Edge Function Zod MessageSchema used by AI-related endpoints so that empty message objects (e.g. {}) are rejected and no longer count toward .min(5) constraints in conversation summary/analysis schemas.

Changes:

  • Made sender required and non-empty (min(1) + max(50)).
  • Made content required (still allows empty string) with max(5000).

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

export const MessageSchema = z.object({
sender: z.string().max(50).optional(),
content: z.string().max(5000).optional(),
sender: z.string().min(1, "sender is required").max(50),
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: 1

🤖 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 16-17: The sender field currently uses z.string().min(1) which
allows strings of only whitespace; normalize the input by trimming before
applying length rules so whitespace-only values fail. Update the sender schema
(the symbol "sender" defined with z.string().min(1,...).max(50)) to trim the
string first (e.g., via .transform(s => s.trim()) or zod's trimming helper) and
then apply min(1, "sender is required") and max(50) so empty or whitespace-only
senders are rejected.
🪄 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: b4bf87b4-0324-4650-bbbb-dfcb6a138915

📥 Commits

Reviewing files that changed from the base of the PR and between 90b09c7 and e1257f5.

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

Comment on lines +16 to +17
sender: z.string().min(1, "sender is required").max(50),
content: z.string().max(5000),
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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

No Zod v3.23.8, z.string().min(1) considera " " (somente espaços) válido sem .trim()?

💡 Result:

Sim, no Zod v3.23.8, z.string.min(1) considera " " (somente espaços) como válido, pois o método min verifica o comprimento da string usando.length, que conta espaços em branco como caracteres. Espaços não são removidos automaticamente sem usar.trim.[1][2][3] A documentação oficial confirma que.min(5) aceita "hello" (5 chars) mas rejeita "hi" (2 chars), e.nonempty é equivalente a.min(1).[1][4] GitHub issues discutem explicitamente que min e max contam espaços: um usuário reclamou que espaços são contados, e a resposta oficial confirma que isso é intencional, pois muitos dependem disso, sugerindo transform para ignorar espaços se necessário.[2] Para rejeitar strings só com espaços, use z.string.trim.min(1).[3][5][6] Nenhuma mudança no comportamento de string.min aparece nos changelogs de v3.23.x.[7][8]

Citations:


sender pode passar vazio com somente espaços em branco.

Na linha 16, z.string().min(1) aceita strings contendo apenas espaços (e.g. " "), pois min() conta espaços como caracteres válidos. Isso contorna a validação de obrigatoriedade do remetente. Normalize a string antes da validação:

Diff
-  sender: z.string().min(1, "sender is required").max(50),
+  sender: z.string().trim().min(1, "sender is required").max(50),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sender: z.string().min(1, "sender is required").max(50),
content: z.string().max(5000),
sender: z.string().trim().min(1, "sender is required").max(50),
content: z.string().max(5000),
🤖 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 16 - 17, The sender field
currently uses z.string().min(1) which allows strings of only whitespace;
normalize the input by trimming before applying length rules so whitespace-only
values fail. Update the sender schema (the symbol "sender" defined with
z.string().min(1,...).max(50)) to trim the string first (e.g., via .transform(s
=> s.trim()) or zod's trimming helper) and then apply min(1, "sender is
required") and max(50) so empty or whitespace-only senders are rejected.

@adm01-debug adm01-debug merged commit c3b2d6c into main May 8, 2026
14 of 15 checks passed
@adm01-debug adm01-debug deleted the chore/schemas-hardening-e1 branch May 8, 2026 18:15
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