CVE-2026-77240 Overview
WACRM, a self-hostable Customer Relationship Management (CRM) template for WhatsApp, contains two database authorization flaws in versions 0.7.0 and earlier. The profiles_update row-level security (RLS) policy allows authenticated users to modify their own account_role and account_id columns, enabling privilege escalation and tenant relocation. Additionally, the match_ai_knowledge_fts and match_ai_knowledge_semantic functions run as SECURITY DEFINER without membership validation, exposing knowledge-base data across tenants. Both weaknesses map to [CWE-639] Authorization Bypass Through User-Controlled Key. The vendor patched the issues in commit e01f7ed37184f972ace8fb2da5c3e37e56a6050f.
Critical Impact
An authenticated viewer can self-promote to owner, move into a victim tenant, and read or modify arbitrary tenant resources including AI knowledge-base chunks.
Affected Products
- WACRM version 0.7.0
- WACRM versions prior to 0.7.0
- Supabase migrations 017_account_sharing.sql and 030_ai_knowledge.sql
Discovery Timeline
- 2026-09-18 - CVE-2026-77240 published to the National Vulnerability Database (NVD)
- 2026-09-23 - CVE-2026-77240 last updated in NVD database
Technical Details for CVE-2026-77240
Vulnerability Analysis
WACRM relies on Supabase Row-Level Security (RLS) to isolate tenant data. Two distinct authorization defects break that isolation.
The first defect lives in the profiles_update RLS policy defined in supabase/migrations/017_account_sharing.sql. The policy gates updates on auth.uid() = user_id, which correctly restricts users to their own row but does not restrict which columns they may modify. Because account_role and account_id reside on the same profiles table and back the is_account_member() check, an authenticated browser client can update those columns directly through PostgREST.
The second defect lives in the AI knowledge-base functions match_ai_knowledge_fts and match_ai_knowledge_semantic in supabase/migrations/030_ai_knowledge.sql. Both are declared SECURITY DEFINER and granted to the authenticated role. They filter on a caller-supplied p_account_id parameter and never invoke is_account_member(), so any logged-in user can request another tenant's chunks.
Root Cause
The policy authors conflated row-scope authorization with column-scope authorization. RLS in PostgreSQL controls which rows a role can read or write, not which columns may change. Sensitive privilege columns were left on the same row as self-service fields without a column-level GRANT or trigger guard. The knowledge-base functions compounded the issue by bypassing RLS entirely under SECURITY DEFINER while trusting a client-controlled tenant identifier.
Attack Vector
An attacker only needs valid credentials for any tenant. From the browser client, the attacker issues an UPDATE against profiles to change account_role to owner or to swap account_id to a victim tenant. Alternatively, the attacker calls /rest/v1/rpc/match_ai_knowledge_fts with a foreign p_account_id to exfiltrate knowledge-base content without altering any state.
-- Vulnerable behavior in profiles_update (migration 017)
-- Viewer self-promotes to owner of the shared account
UPDATE profiles SET account_role = 'owner' WHERE user_id = auth.uid();
-- Attacker relocates their profile into a victim tenant
UPDATE profiles SET account_id = '<victim>' WHERE user_id = auth.uid();
-- Cross-tenant knowledge-base read via PostgREST RPC (migration 030)
-- POST /rest/v1/rpc/match_ai_knowledge_fts
-- { "p_account_id": "<victim>", "p_query": "price", "p_match_count": 1000 }
Source: GitHub Commit e01f7ed
Detection Methods for CVE-2026-77240
Indicators of Compromise
- UPDATE statements against the profiles table that modify account_role or account_id and originate from the authenticated role.
- PostgREST RPC calls to match_ai_knowledge_fts or match_ai_knowledge_semantic where p_account_id differs from the caller's tenant.
- Sudden appearance of a user in a tenant they were never invited to, visible in profiles audit history.
Detection Strategies
- Enable PostgreSQL audit logging (pgaudit) on the profiles table to capture every column-level change to account_role and account_id.
- Add a database-side trigger that logs any RPC invocation of the knowledge-base functions with the caller sub, p_account_id, and result row count.
- Correlate authentication logs from Supabase Auth with unexpected tenant switches recorded in application telemetry.
Monitoring Recommendations
- Alert when the same user identifier appears bound to multiple account_id values across a short time window.
- Baseline the volume and diversity of p_account_id values requested per user against the knowledge-base RPC endpoints.
- Forward Supabase and PostgREST logs to a centralized analytics pipeline for cross-tenant anomaly analysis.
How to Mitigate CVE-2026-77240
Immediate Actions Required
- Upgrade WACRM to a version that includes commit e01f7ed37184f972ace8fb2da5c3e37e56a6050f or apply migrations 031_fix_profiles_update_rls.sql and 032_fix_ai_knowledge_membership.sql.
- Audit the profiles table for unexpected account_role = 'owner' assignments or account_id reassignments performed by non-administrative users.
- Rotate credentials and invalidate active sessions for any user identified as having pivoted between tenants.
Patch Information
The fix is delivered in two migrations. Migration 031_fix_profiles_update_rls.sql installs a BEFORE UPDATE trigger that rejects changes to account_role and account_id when the caller is the authenticated role, while allowing the legitimate handle_new_user flow and the member and invitation Remote Procedure Calls (RPCs) to continue. Migration 032_fix_ai_knowledge_membership.sql recreates match_ai_knowledge_fts and match_ai_knowledge_semantic as SECURITY INVOKER so the existing ai_knowledge_chunks_select policy governs authenticated callers and returns zero rows for foreign p_account_id values. Full details are in GitHub Security Advisory GHSA-fg5p-2qc3-jmxr and Pull Request #350.
Workarounds
- Revoke UPDATE on profiles.account_role and profiles.account_id from the authenticated role using column-level REVOKE statements until the patch is applied.
- Revoke EXECUTE on match_ai_knowledge_fts and match_ai_knowledge_semantic from the authenticated role and restrict invocation to the service_role.
- Disable public exposure of the affected PostgREST RPC endpoints via API gateway or reverse-proxy rules.
# Emergency mitigations before applying the official patch
psql "$DATABASE_URL" <<'SQL'
REVOKE UPDATE (account_role, account_id) ON public.profiles FROM authenticated;
REVOKE EXECUTE ON FUNCTION public.match_ai_knowledge_fts FROM authenticated;
REVOKE EXECUTE ON FUNCTION public.match_ai_knowledge_semantic FROM authenticated;
SQL
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

