Skip to content

fix(security): Onda 20 — RE-GRANT t38 regression + bilateral SECURITY DEFINER gate#224

Merged
adm01-debug merged 1 commit into
mainfrom
onda-20-fix-t38-regression-bilateral-gate
May 15, 2026
Merged

fix(security): Onda 20 — RE-GRANT t38 regression + bilateral SECURITY DEFINER gate#224
adm01-debug merged 1 commit into
mainfrom
onda-20-fix-t38-regression-bilateral-gate

Conversation

@adm01-debug
Copy link
Copy Markdown
Owner

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

Onda 20 — Eliminar a categoria inteira de bug SECURITY DEFINER + RLS

Contexto

CI do run 25917921566 (PR #221) ficou vermelho em 2 jobs após PR #192 / t38 ter sido mergeado hoje cedo:

  • Lint, Typecheck & Test → step 🔐 SECURITY DEFINER ACL gate (link compartilhado pelo PO)
  • Test CoverageRun tests with coverage (escopo da Onda 21, fora desta PR)

Investigação descobriu 2 bugs independentes que se combinaram:

Bug 1 — Regressão crítica em runtime (severidade > gate)

A migration 20260514000001_t38_deploy_hardening_final.sql revogou EXECUTE de authenticated em:

  • public.is_admin_or_above(uuid)usada em 83 policies RLS
  • public.is_coord_or_above(uuid)usada em 29 policies RLS

Confirmado em prod (doufsxqlfjyuvxuezpln):

SET LOCAL role authenticated; SELECT count(*) FROM integration_credentials;
→ ERROR: 42501: permission denied for function is_admin_or_above

Toda operação de usuário autenticado em ≥112 contextos de RLS está quebrada desde t38 ter sido aplicada. Sistema não está em prod real ainda (memória do PO), nenhum cliente afetado — mas tem que entrar verde antes do go-live.

Bug 2 — Gate falhando legitimamente

public.org_has_any_members(uuid), criada por 20260514000000_fix_policy_idempotency_and_security.sql, sem REVOKE FROM PUBLIC. PostgreSQL grantia EXECUTE pra PUBLIC + anon por default → audit_security_definer_acl() reporta 2 violações.

Causa raiz arquitetural — gate unilateral

O gate atual (audit_security_definer_acl()) detecta excesso de privilégio (PUBLIC/anon), mas não detecta carência (função SECURITY DEFINER usada em policy sem EXECUTE pra authenticated). Por isso PR #192 passou no CI pré-merge. Bug não pegaria em revisão humana sem rodar prod.

O que esta PR faz

Migration supabase/migrations/20260515150000_onda20_fix_t38_regression_and_bilateral_gate.sql (162 linhas):

  1. GRANT EXECUTE TO authenticated em is_admin_or_above e is_coord_or_above → desbloqueia 112 policies
  2. REVOKE FROM PUBLIC e REVOKE FROM anon em org_has_any_members (mantém authenticated + service_role)
  3. Reescreve audit_security_definer_acl() com gate bilateral — 4 categorias em vez de 3:
    • Caso 1: PUBLIC com EXECUTE (existente)
    • Caso 2: anon com EXECUTE fora da whitelist (existente)
    • Caso 3: trigger function com EXECUTE pra authenticated (existente)
    • Caso 4 (NOVO): SECURITY DEFINER citada em pg_policy sem EXECUTE pra authenticated — RLS quebra com 42501
  4. RAISE EXCEPTION fail-fast no final: se sobrar violação após a cirurgia, transação inteira aborta (rollback)

Doc novo docs/SECURITY-DEFINER-PATTERN.md (164 linhas):

  • 3 templates canônicos: RLS helper, RPC backend-only, public-intent
  • Explicação de por que SECURITY DEFINER não dispensa GRANT pro caller
  • Workflow recomendado pra criar nova função (checklist de 7 passos)
  • Histórico (Onda 20, t28 pilot, hardening original)

Validação

Dry-run em prod (DO block com RAISE para rollback intencional)

GRANT EXECUTE ON FUNCTION public.is_admin_or_above(_user_id uuid) TO authenticated;  -- OK
GRANT EXECUTE ON FUNCTION public.is_coord_or_above(_user_id uuid) TO authenticated;  -- OK
REVOKE EXECUTE ON FUNCTION public.org_has_any_members(_org_id uuid) FROM PUBLIC;      -- OK
REVOKE EXECUTE ON FUNCTION public.org_has_any_members(_org_id uuid) FROM anon;        -- OK

Inventário pré-fix vs pós-fix esperado

Função Pré (broken) Pós (fix)
is_admin_or_above ✗ authenticated ✅ authenticated
is_coord_or_above ✗ authenticated ✅ authenticated
org_has_any_members ⚠️ PUBLIC + anon ✅ só authenticated + service_role
can_access_quote ok (Onda 18) ok

Nenhuma outra função SECURITY DEFINER em public está em estado inconsistente — confirmado por query exaustiva contra prod.

Plano de Ondas seguintes (decisão prévia do PO)

  • Onda 21 — fix Test Coverage failure (job separado, threshold ou arquivo novo) — PR à parte
  • Onda 22docs/HANDOFF-AUDIT-CLOSURE-2026-05-15.md consolidando os 63 achados da auditoria de 10/mai → PR/decisão/data, escudo contra perda de contexto entre sessões Claude

Risco / Rollback

  • Migration é idempotente (GRANT é idempotente; REVOKE de role inexistente é no-op)
  • RAISE EXCEPTION no fail-fast aborta automaticamente se algo escapou
  • Função audit_security_definer_acl() é SECURITY INVOKER (não toca o gate check-security-definer-hardening.mjs)
  • Sem prod real → janela de rollback simples (revert da PR)

Refs:


Summary by cubic

Fixes the t38 RLS regression by restoring EXECUTE for authenticated on public.is_admin_or_above and public.is_coord_or_above, and revoking PUBLIC/anon on public.org_has_any_members. Adds a bilateral audit_security_definer_acl() so CI catches both over- and under-privileged SECURITY DEFINER functions, plus docs with canonical patterns.

  • Bug Fixes

    • Re-granted EXECUTE to authenticated on is_admin_or_above(uuid) and is_coord_or_above(uuid) to unblock 112 RLS policies (fixed 42501 errors).
    • Removed default PUBLIC/anon EXECUTE on org_has_any_members(uuid) to close the ACL gate violation.
  • New Features

    • Rewrote audit_security_definer_acl() to add Case 4: function used in RLS but missing authenticated EXECUTE; includes fail-fast RAISE EXCEPTION if violations remain.
    • Added docs/SECURITY-DEFINER-PATTERN.md with templates and a checklist for SECURITY DEFINER functions (RLS helper, backend-only RPC, public-intent).

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

Summary by CodeRabbit

Notas de Lançamento

  • Documentação

    • Novo padrão de hardening estabelecido para funções de segurança do sistema, formalizando boas práticas e requisitos de conformidade.
  • Correções

    • Corrigido problema de regressão que impedia funcionamento de funções administrativas críticas.
    • Reforçado controle de permissões para funções de validação de membros organizacionais.
    • Aprimorada auditoria de segurança para detectar configurações incorretas de permissões.

Review Change Stack

… DEFINER gate

Fixes a critical RLS regression introduced by PR #192 (t38_deploy_hardening_final)
which revoked EXECUTE on is_admin_or_above and is_coord_or_above from
authenticated. These functions are used in 83+29 RLS policies, so every
logged-in operation broke with `42501: permission denied for function`.

Also closes the SECURITY DEFINER ACL gate failure on org_has_any_members
(created without REVOKE FROM PUBLIC/anon by 20260514000000).

Root fix: rewrites audit_security_definer_acl() to detect 4 categories
instead of 3, adding Caso 4 — SECURITY DEFINER citada em pg_policy sem
EXECUTE para authenticated (the exact anti-pattern that t38 created and
that the old gate did not catch).

Docs: docs/SECURITY-DEFINER-PATTERN.md — canonical templates for the 3
valid patterns (RLS helper, RPC backend-only, public-intent).

claude-code@atomicabr.com.br
Copilot AI review requested due to automatic review settings May 15, 2026 12:56
@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

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

Project Deployment Actions Updated (UTC)
promo-gifts Ready Ready Preview, Comment May 15, 2026 0:57am

@supabase
Copy link
Copy Markdown

supabase Bot commented May 15, 2026

This pull request has been ignored for the connected project doufsxqlfjyuvxuezpln due to reaching the limit of concurrent preview branches.
Go to Project Integrations Settings ↗︎ if you wish to update this limit.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

Walkthrough

Migração que formaliza padrão de hardening SECURITY DEFINER com novo documento de referência, reescreve audit_security_definer_acl() como bilateral gate detectando 4 categorias de violação (PUBLIC, anon, trigger, RLS policy), corrige regressões t38 em is_admin_or_above/is_coord_or_above, restringe org_has_any_members, e executa validação fail-fast.

Changes

Hardening SECURITY DEFINER com bilateral audit gate

Layer / File(s) Summary
Documentação do padrão SECURITY DEFINER
docs/SECURITY-DEFINER-PATTERN.md
Novo documento formaliza categorias A/B/C, templates SQL com REVOKE/GRANT, SET search_path, 4 regras do gate com mensagens específicas, workflow para novas funções e histórico da Onda 20.
Implementação do bilateral audit gate
supabase/migrations/20260515150000_onda20_fix_t38_regression_and_bilateral_gate.sql (linhas 58-144)
Reescrita de public.audit_security_definer_acl() com SECURITY INVOKER, varredura de pg_proc/pg_policies, detecção de 4 anti-padrões (PUBLIC com EXECUTE, anon fora whitelist, trigger incorretas, RLS policies sem authenticated), comentário atualizado e grants para authenticated/service_role.
Correções de permissões e regressões
supabase/migrations/20260515150000_onda20_fix_t38_regression_and_bilateral_gate.sql (linhas 43-52)
Re-grant EXECUTE para authenticated em is_admin_or_above(_user_id uuid) e is_coord_or_above(_user_id uuid) (regressão t38); revoke de PUBLIC e anon em org_has_any_members(_org_id uuid).
Fail-fast validation em migração
supabase/migrations/20260515150000_onda20_fix_t38_regression_and_bilateral_gate.sql (linhas 149-167)
Bloco DO $$ consulta audit_security_definer_acl(), contabiliza violações, agrega detalhes, lança RAISE EXCEPTION com violações ou RAISE NOTICE quando limpo.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • adm01-debug/Promo_Gifts#204: Reconfigura SECURITY DEFINER/gate e permissões EXECUTE de org_has_any_members ao montar RLS policies—sobreposição direta no mesmo objeto SQL e efeitos de grants/policies.
  • adm01-debug/Promo_Gifts#169: Altera is_admin_or_above e is_coord_or_above via SECURITY INVOKER e permissões relacionadas—conexão direta no nível de funções RLS helpers.
  • adm01-debug/Promo_Gifts#213: Cria public.can_access_quote() e ajusta GRANT/REVOKE EXECUTE para que policies RLS que chamam a função passem pelo gate audit reescrito.
🚥 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 reflete precisamente as duas correções principais da PR: fix de regressão t38 (RE-GRANT de EXECUTE) e implementação do gate bilateral de SECURITY DEFINER, alinhado com os objetivos da Onda 20.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch onda-20-fix-t38-regression-bilateral-gate

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 addresses a production-breaking RLS regression caused by missing EXECUTE grants on SECURITY DEFINER helpers, and strengthens the CI “SECURITY DEFINER ACL gate” by adding a new detection case for SECURITY DEFINER functions referenced by RLS policies without EXECUTE for authenticated.

Changes:

  • Re-grants EXECUTE to authenticated for is_admin_or_above(uuid) and is_coord_or_above(uuid) to restore RLS evaluation.
  • Revokes EXECUTE on org_has_any_members(uuid) from PUBLIC and anon.
  • Rewrites audit_security_definer_acl() to add a new “used in RLS policy but missing EXECUTE for authenticated” detection case and adds a fail-fast RAISE EXCEPTION block.
  • Adds documentation describing canonical SECURITY DEFINER patterns and the gate behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
supabase/migrations/20260515150000_onda20_fix_t38_regression_and_bilateral_gate.sql Fixes function grants involved in RLS breakage and expands the SECURITY DEFINER ACL audit gate with a new “missing EXECUTE for authenticated” category plus fail-fast behavior.
docs/SECURITY-DEFINER-PATTERN.md Documents the SECURITY DEFINER + RLS patterns and the expected GRANT/REVOKE workflow aligned with the CI gate.

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

-- (criada por 20260514000000 com default PUBLIC do PostgreSQL)
-- ─────────────────────────────────────────────────────────────────
REVOKE EXECUTE ON FUNCTION public.org_has_any_members(_org_id uuid) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION public.org_has_any_members(_org_id uuid) FROM anon;
Comment on lines +91 to +96
FROM defs d
LEFT JOIN LATERAL (
SELECT (aclexplode(d.proacl)).grantee
) a ON true
WHERE a.grantee IS NOT NULL
),
REVOKE EXECUTE ON FUNCTION public.minha_rpc_admin(text) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION public.minha_rpc_admin(text) FROM anon;
REVOKE EXECUTE ON FUNCTION public.minha_rpc_admin(text) FROM authenticated;
-- service_role mantém grant default
Copy link
Copy Markdown
Contributor

@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: 2

🤖 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 `@docs/SECURITY-DEFINER-PATTERN.md`:
- Around line 32-34: O bloco de erro no documento está sem linguagem e aciona o
lint MD040; edite o fence que contém "ERROR: 42501: permission denied for
function is_admin_or_above" para declarar a linguagem, por exemplo trocando ```
para ```text de forma que o bloco comece com ```text e termine com ```,
garantindo que o código seja tratado como texto e o warning seja eliminado.

In
`@supabase/migrations/20260515150000_onda20_fix_t38_regression_and_bilateral_gate.sql`:
- Around line 83-96: O CTE acl_expanded usa aclexplode(d.proacl) e ignora
objetos com proacl IS NULL (ACL implícita), por isso a função
audit_security_definer_acl() não detecta permissões efetivas vindas de
acldefault; corrija substituindo/estendendo a expansão para também usar
acldefault quando proacl IS NULL (por exemplo aclexplode(coalesce(d.proacl,
acldefault(...))) ou fazendo um UNION LATERAL com aclexplode(acldefault(...)))
garantindo que a lógica que verifica grantee/public_intent em acl_expanded (e o
mesmo padrão aplicado ao caso 4) passe a considerar as permissões default além
das ACLs explícitas.
🪄 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: cce0878e-5c7e-467c-a735-12415ca60aae

📥 Commits

Reviewing files that changed from the base of the PR and between 840f2ef and ef1be60.

📒 Files selected for processing (2)
  • docs/SECURITY-DEFINER-PATTERN.md
  • supabase/migrations/20260515150000_onda20_fix_t38_regression_and_bilateral_gate.sql

Comment on lines +32 to +34
```
ERROR: 42501: permission denied for function is_admin_or_above
```
Copy link
Copy Markdown
Contributor

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

Defina linguagem no bloco cercado para eliminar warning de lint.

O bloco de erro está sem linguagem e dispara MD040.

💡 Ajuste sugerido
-```
+```text
 ERROR: 42501: permission denied for function is_admin_or_above
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.22.1)</summary>

[warning] 32-32: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

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

In @docs/SECURITY-DEFINER-PATTERN.md around lines 32 - 34, O bloco de erro no
documento está sem linguagem e aciona o lint MD040; edite o fence que contém
"ERROR: 42501: permission denied for function is_admin_or_above" para declarar a
linguagem, por exemplo trocando paratext de forma que o bloco comece com
text e termine com , garantindo que o código seja tratado como texto e o
warning seja eliminado.


</details>

<!-- fingerprinting:phantom:poseidon:hawk -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment on lines +83 to +96
acl_expanded AS (
SELECT
d.oid,
d.proname,
d.args,
d.is_trigger,
d.public_intent,
a.grantee::regrole::text AS grantee
FROM defs d
LEFT JOIN LATERAL (
SELECT (aclexplode(d.proacl)).grantee
) a ON true
WHERE a.grantee IS NOT NULL
),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

git ls-files "supabase/migrations/*.sql" | head -20

Repository: adm01-debug/Promo_Gifts

Length of output: 1161


🏁 Script executed:

cat -n supabase/migrations/20260515150000_onda20_fix_t38_regression_and_bilateral_gate.sql | head -150

Repository: adm01-debug/Promo_Gifts

Length of output: 7627


🏁 Script executed:

# Verificar tamanho do arquivo para leitura completa
wc -l supabase/migrations/20260515150000_onda20_fix_t38_regression_and_bilateral_gate.sql

Repository: adm01-debug/Promo_Gifts

Length of output: 155


🏁 Script executed:

cat -n supabase/migrations/20260515150000_onda20_fix_t38_regression_and_bilateral_gate.sql | tail -30

Repository: adm01-debug/Promo_Gifts

Length of output: 1743


🏁 Script executed:

# Verificar se existem funções SECURITY DEFINER no public com proacl NULL
# usando readymade queries PostgreSQL (syntax check)
cat > /tmp/test_acl_logic.sql << 'EOF'
-- Teste 1: aclexplode() com NULL
SELECT COUNT(*) FROM aclexplode(NULL::aclitem[]);

-- Teste 2: acldefault() para função
SELECT acldefault('f'::char, 1::oid);

-- Teste 3: Comparar resultados
WITH test_null AS (
  SELECT (aclexplode(NULL::aclitem[])).grantee AS grantee_null
),
test_default AS (
  SELECT (aclexplode(acldefault('f'::char, 1::oid))).grantee AS grantee_default
)
SELECT 
  (SELECT COUNT(*) FROM test_null) AS null_count,
  (SELECT COUNT(*) FROM test_default) AS default_count;
EOF
cat /tmp/test_acl_logic.sql

Repository: adm01-debug/Promo_Gifts

Length of output: 560


🏁 Script executed:

# Verificar a doc PostgreSQL: aclexplode() behavior
# Além disso, verificar se há referência à proacl no código ou comentários
rg "proacl|acldefault|aclexplode" --type sql supabase/migrations/ -n

Repository: adm01-debug/Promo_Gifts

Length of output: 521


🌐 Web query:

PostgreSQL aclexplode NULL behavior proacl functions default permissions

💡 Result:

In PostgreSQL, the aclexplode function is a system function used to decompose an aclitem array (such as those found in system catalog columns like relacl) into a set of rows, where each row represents a specific privilege grant [1][2]. Regarding NULL behavior and default permissions: 1. NULL in ACL Columns: When an ACL column (like relacl in pg_class) is NULL, it signifies that the object is using the system's hard-wired default privileges [3][4]. It does not mean that no privileges exist; rather, it indicates that the default access rules apply [3]. 2. aclexplode and NULLs: The aclexplode function does not automatically resolve NULL values into the hard-wired default privileges [5]. If you pass a NULL value to aclexplode, it will return no rows, as there are no explicit grants to explode [1][5]. 3. Default Privileges: To determine the effective privileges for an object, you must account for both explicit grants (stored in the ACL column) and the implicit hard-wired defaults [3][6]. Because aclexplode only processes the explicit ACL array, it is not sufficient on its own to reconstruct the full set of effective permissions if the ACL column is NULL [5]. 4. Handling NULLs: If you need to programmatically determine effective permissions, you must implement logic to check if the ACL column is NULL and, if so, apply the corresponding hard-wired default privileges for that object type [5][3]. The system catalog pg_default_acl is used for user-defined default privileges (set via ALTER DEFAULT PRIVILEGES), but it is only consulted during object creation and does not replace the hard-wired defaults for existing objects [3][7][6]. In summary, aclexplode only explodes explicit grants [1][8]. A NULL value in an ACL column represents the presence of hard-wired default privileges, which aclexplode does not expand [5][3].

Citations:


🏁 Script executed:

# Verificar se proowner está disponível e check lógica proposta
rg "proowner" supabase/migrations/ -A 2 -B 2

Repository: adm01-debug/Promo_Gifts

Length of output: 962


🏁 Script executed:

# Procurar por CREATE FUNCTION sem GRANT explícito nas migrations
rg "CREATE.*FUNCTION.*public\." supabase/migrations/ | head -20

Repository: adm01-debug/Promo_Gifts

Length of output: 2996


🏁 Script executed:

# Verificar funções SECURITY DEFINER públicas neste repo
rg "CREATE.*FUNCTION.*public\." supabase/migrations/ -A 5 | grep -E "CREATE|SECURITY DEFINER" | head -40

Repository: adm01-debug/Promo_Gifts

Length of output: 4747


🏁 Script executed:

# Buscar especificamente por funções SECURITY DEFINER que não têm GRANT explícito logo após
rg "SECURITY DEFINER" supabase/migrations/ -B 2 -A 8 | head -80

Repository: adm01-debug/Promo_Gifts

Length of output: 7464


Gate de auditoria ACL deixa falso negativo: proacl IS NULL não é expandido para permissões efetivas default.

O gate audit_security_definer_acl() usa aclexplode(d.proacl) em acl_expanded, mas quando proacl IS NULL (ACL hard-wired default, não explícita), aclexplode não retorna linhas. Funções SECURITY DEFINER sem GRANT explícito têm proacl=NULL por padrão e herdam permissões efetivas via acldefault()—casos que o gate não detecta.

Risco crítico: Funções com PUBLIC EXECUTE efetivo podem passar pelo gate sem alerta, replicando o problema que a migration tenta corrigir.

Patch objetivo
  WITH defs AS (
    SELECT
      p.oid,
      p.proname,
      pg_get_function_identity_arguments(p.oid) AS args,
+     p.proowner,
      p.proacl,
      (pg_get_function_result(p.oid) = 'trigger') AS is_trigger,
      (p.proname IN ('submit_quote_response', 'get_quote_token_by_value')) AS public_intent
    FROM pg_proc p
    JOIN pg_namespace n ON n.oid = p.pronamespace
    WHERE n.nspname = 'public' AND p.prosecdef = true
  ),
  acl_expanded AS (
    SELECT
      d.oid,
      d.proname,
      d.args,
      d.is_trigger,
      d.public_intent,
      a.grantee::regrole::text AS grantee
    FROM defs d
    LEFT JOIN LATERAL (
-     SELECT (aclexplode(d.proacl)).grantee
+     SELECT (aclexplode(COALESCE(d.proacl, acldefault('f', d.proowner)))).grantee
    ) a ON true
    WHERE a.grantee IS NOT NULL
  ),

Também afeta Caso 4 (linhas 128–137): funções em policies sem EXECUTE pra authenticated não serão detectadas se usarem ACL default.

🤖 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/migrations/20260515150000_onda20_fix_t38_regression_and_bilateral_gate.sql`
around lines 83 - 96, O CTE acl_expanded usa aclexplode(d.proacl) e ignora
objetos com proacl IS NULL (ACL implícita), por isso a função
audit_security_definer_acl() não detecta permissões efetivas vindas de
acldefault; corrija substituindo/estendendo a expansão para também usar
acldefault quando proacl IS NULL (por exemplo aclexplode(coalesce(d.proacl,
acldefault(...))) ou fazendo um UNION LATERAL com aclexplode(acldefault(...)))
garantindo que a lógica que verifica grantee/public_intent em acl_expanded (e o
mesmo padrão aplicado ao caso 4) passe a considerar as permissões default além
das ACLs explícitas.

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: ef1be60522

ℹ️ 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".

Comment on lines +50 to +51
REVOKE EXECUTE ON FUNCTION public.org_has_any_members(_org_id uuid) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION public.org_has_any_members(_org_id uuid) FROM anon;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Guard the org_has_any_members revoke

In preview/partial schemas where 20260514000000_fix_policy_idempotency_and_security.sql skipped creating org_has_any_members because organization_members did not exist (that migration wraps the function creation in an IF EXISTS table check), this unconditional REVOKE EXECUTE ON FUNCTION public.org_has_any_members(...) aborts with “function does not exist” before the rest of the migration can run. The surrounding migrations are explicitly defensive for those environments, so this fix should also check pg_proc or use dynamic guarded SQL before revoking from this function.

Useful? React with 👍 / 👎.

Comment on lines +43 to +44
GRANT EXECUTE ON FUNCTION public.is_admin_or_above(_user_id uuid) TO authenticated;
GRANT EXECUTE ON FUNCTION public.is_coord_or_above(_user_id uuid) TO authenticated;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Guard the role-helper grants

In any preview or rebuilt environment that does not already have these legacy helper functions, these unconditional GRANT EXECUTE ON FUNCTION ... statements abort with “function does not exist” before the migration reaches the new ACL gate. The preceding t38 migration used pg_proc checks before touching the same functions, so this repair migration needs the same guard or dynamic SQL to remain safe for the partial schemas this migration set already supports.

Useful? React with 👍 / 👎.

(p.proname IN ('submit_quote_response', 'get_quote_token_by_value')) AS public_intent
FROM pg_proc p
JOIN pg_namespace n ON n.oid = p.pronamespace
WHERE n.nspname = 'public' AND p.prosecdef = 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.

P1 Badge Include invoker RLS helpers in the missing-grant gate

Because defs is restricted to p.prosecdef = true, Case 4 never inspects SECURITY INVOKER functions used by RLS policies. In this repo 20260513000001_t37a_security_invoker_safe_batch.sql already converted both is_admin_or_above(uuid) and is_coord_or_above(uuid) to SECURITY INVOKER, so this new gate would not have caught the exact t38 revocation regression it is meant to prevent if the explicit re-grants were removed or a future migration repeats the revoke.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

No issues found across 2 files

@adm01-debug adm01-debug merged commit 52710b8 into main May 15, 2026
26 of 30 checks passed
@adm01-debug adm01-debug deleted the onda-20-fix-t38-regression-bilateral-gate branch May 15, 2026 13:09
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