Skip to content

fix(security): fecha IDOR de QR/instance do WhatsApp e remove 4 funções mortas - #125

Merged
adm01-debug merged 3 commits into
mainfrom
fix/idor-connection-qr-and-dead-secdef
Sep 2, 2026
Merged

fix(security): fecha IDOR de QR/instance do WhatsApp e remove 4 funções mortas#125
adm01-debug merged 3 commits into
mainfrom
fix/idor-connection-qr-and-dead-secdef

Conversation

@adm01-debug

@adm01-debug adm01-debug commented Sep 1, 2026

Copy link
Copy Markdown
Owner

M2 — IDOR (auditoria 28/08/2026)

get_connection_qr_code(uuid) e get_connection_instance(uuid) são SECURITY DEFINER, EXECUTE concedido a authenticated, sem checagem de posse/role. A tabela whatsapp_connections já restringe SELECT a admin/supervisor via RLS (Admin supervisor view connections = is_admin_or_supervisor(auth.uid())), mas SECURITY DEFINER ignora RLS — qualquer agente logado podia chamar supabase.rpc('get_connection_qr_code', {...}) e ler o QR code de pareamento do WhatsApp (risco de sequestro de sessão) ou o instance_id, sem passar pela policy.

Zero chamadas a essas RPCs no front hoje (grep confirma) — fechado por defesa em profundidade, com o mesmo guard já usado na policy da própria tabela.

Testado ao vivo por simulação (request.jwt.claims, sem mutação persistida):

agent  → get_connection_instance(...) = NULL
admin  → get_connection_instance(...) = 'PRINCIPAL'

B1/B6 — 4 funções mortas reativáveis

  • prevent_role_escalation — a barreira real de escalação de privilégio hoje é audit_role_changes + guard direto em profiles/user_roles (migrations 20260830010000/20260830030000). 0 triggers referenciam esta função. Religá-la reabriria o bug A-01 (reversão silenciosa de escalação documentado na auditoria de 27/08).
  • mask_channel_credentials — stub de trigger ("credentials masking é feito via view segura"), 0 triggers a referenciam.
  • get_reset_requests_safe — referencia a coluna inexistente password_reset_requests.reset_token (42703 se chamada). Resto do subsistema de reset customizado já foi dropado na migration 20260827170000.
  • get_own_reset_requests — mesma família morta, zero chamadas no front.

Zero dependentes confirmados via pg_depend antes do DROP FUNCTION.

Aplicação

DDL aplicada direto em produção (tnnnlkbymytvtqngbbqh) e registrada em supabase_migrations.schema_migrations (protocolo do CLAUDE.md — supabase_apply_migration bugado neste ambiente). O statements do ledger é byte-idêntico ao arquivo desta migration, então o DB Live Guard deve validar por conteúdo canônico direto, sem precisar de exceção em migration-evidence.json.

schema-catalog.json/schema-manifest.json/types.ts não foram tocados aqui — seguindo o padrão já estabelecido neste repo, o workflow types-sync detecta o drift no push para main e abre o PR de sincronização automaticamente.

Validação

  • node scripts/db-audit/check-migration-drift.mjs → offline OK (327 arquivos válidos; comparação com o ledger vivo roda no DB Live Guard pós-merge).
  • node scripts/db-audit/supabase-usage-guard.mjs → 0 violações novas.
  • node --test scripts/db-audit/*.test.mjs → 118/118 passam.

🤖 Generated with Claude Code

https://claude.ai/code/session_018N9zUcTpab3dWsuR3ZSSBj


Summary by cubic

Fixes an IDOR in the WhatsApp connection RPCs where any authenticated agent could read pairing QR codes or instance IDs, and drops four dead functions that could re-open known security bugs.

Bug Fixes

  • get_connection_qr_code and get_connection_instance now return NULL unless the caller is admin or supervisor; previously SECURITY DEFINER bypassed RLS.
  • Dropped prevent_role_escalation, mask_channel_credentials, get_reset_requests_safe, and get_own_reset_requests — none have live triggers, callers, or dependent objects.
  • Re-enabling prevent_role_escalation would reintroduce the A-01 silent privilege escalation bug.

Migration

  • DDL applied directly to production and recorded in supabase_migrations.schema_migrations.
  • No frontend calls these RPCs, so no app behavior change; types-sync will open the schema sync PR on merge.

Written for commit c0dfe8b. Summary will update on new commits.

Review in cubic

…es mortas

Aplicado direto em produção (tnnnlkbymytvtqngbbqh) e registrado no ledger
via supabase_migrations.schema_migrations, seguindo o protocolo de
supabase_apply_migration bugado descrito no CLAUDE.md.

M2 (IDOR, auditoria 28/08/2026): get_connection_qr_code e
get_connection_instance são SECURITY DEFINER, EXECUTE concedido a
'authenticated', sem checagem de posse/role. A tabela
whatsapp_connections já restringe SELECT a admin/supervisor via RLS,
mas SECURITY DEFINER ignora RLS — qualquer agente logado podia chamar
supabase.rpc('get_connection_qr_code', {...}) e ler o QR code de
pareamento (risco de sequestro de sessão WhatsApp) ou o instance_id.
Zero chamadas no front hoje (grep confirma); fechado por defesa em
profundidade com o mesmo guard já usado na policy da tabela
(is_admin_or_supervisor(auth.uid())). Testado ao vivo por simulação
com request.jwt.claims: agent → NULL, admin → valor real.

B1/B6: remove 4 funções mortas reativáveis apontadas na mesma
auditoria — prevent_role_escalation e mask_channel_credentials
(0 triggers as referenciam), get_reset_requests_safe (referencia a
coluna inexistente password_reset_requests.reset_token, 42703 se
chamada) e get_own_reset_requests (resto do subsistema de reset
customizado já dropado em 20260827170000). Zero dependentes
confirmados via pg_depend antes do DROP.

schema-catalog.json/schema-manifest.json/types.ts não foram tocados
neste PR — o workflow types-sync detecta o drift no push para main e
abre o PR de sincronização automaticamente (mesmo padrão de todo PR
anterior de migration neste repo).

Validado: check-migration-drift.mjs (offline OK, ledger não
comparado sem DESTINO_URL), supabase-usage-guard (0 violações novas),
118/118 testes de scripts/db-audit/*.test.mjs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018N9zUcTpab3dWsuR3ZSSBj
Copilot AI lite review requested due to automatic review settings September 1, 2026 23:42
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated
zapp_web_v2 Ready Ready Preview Sep 1, 2026 11:59pm UTC

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

adm01-debug has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 7 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 99 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 08ef9073-57b8-4ea0-913f-8bc880b7e242

📥 Commits

Reviewing files that changed from the base of the PR and between 2b335f1 and c0dfe8b.

📒 Files selected for processing (1)
  • supabase/migrations/20260901210000_fix_connection_idor_and_drop_dead_secdef.sql

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Do jeito que está (DDL já aplicado no DB vivo, mas sem atualizar types.ts/schema-catalog/schema-manifest), o DB Live Guard em push para main tende a falhar por drift até a sincronização ser mergeada.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Este PR endurece o acesso a dados sensíveis de conexões WhatsApp no banco (Supabase) corrigindo um vetor de IDOR em RPCs SECURITY DEFINER e removendo funções órfãs/reativáveis que não têm dependentes ativos.

Changes:

  • Adiciona checagem is_admin_or_supervisor(auth.uid()) dentro de get_connection_qr_code(uuid) e get_connection_instance(uuid) para impedir leitura por usuários autenticados sem privilégio.
  • Remove 4 funções mortas (prevent_role_escalation, mask_channel_credentials, get_reset_requests_safe, get_own_reset_requests) via DROP FUNCTION IF EXISTS.
File summaries
File Description
supabase/migrations/20260901210000_fix_connection_idor_and_drop_dead_secdef.sql Corrige IDOR em RPCs de WhatsApp e remove funções SQL órfãs/arriscadas.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="supabase/migrations/20260901210000_fix_connection_idor_and_drop_dead_secdef.sql">

<violation number="1" location="supabase/migrations/20260901210000_fix_connection_idor_and_drop_dead_secdef.sql:14">
P3: Both RPCs are recreated as SECURITY DEFINER with EXECUTE still granted to authenticated, even though the PR confirms zero front-end callers. The `is_admin_or_supervisor(auth.uid())` gate is correct, but keeping a SECURITY DEFINER function callable by every authenticated user preserves the attack surface this PR set out to close — any future regression in the gate (or a new caller) silently reopens the IDOR. Since there are zero callers, the strongest remediation is to drop these functions too (same pattern as the 4 dead functions in this migration) or at minimum `REVOKE EXECUTE ... FROM authenticated, public`, re-creating them only when a real caller exists.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

adm01-debug has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

adm01-debug has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@adm01-debug
adm01-debug merged commit 17436eb into main Sep 2, 2026
13 checks passed
@adm01-debug
adm01-debug deleted the fix/idor-connection-qr-and-dead-secdef branch September 2, 2026 00:03
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.

3 participants