Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ commits = [
"4f3af41dcf113613180d968b26b6c706c82048b9",
"aaa508e40e323973ee535348a871b68883c49932",
"4a3f3d2756899aab26820cb7825348d98dc60c46",
# Commit que introduziu docs com anon keys de exemplo em PROMPT_LOVABLE e SELF-HOSTED-DATABASE-GUIDE.
# Inspecionado manualmente — são anon keys (sem service_role) já expostas publicamente;
# os tokens foram substituídos por placeholders no commit seguinte.
"0097a8bef7ad5026c2275edede9582b078be8a44",
]

# sha256-<hash> em Content-Security-Policy são nonces de scripts inline permitidos,
Expand Down
91 changes: 85 additions & 6 deletions bun.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/PROMPT_LOVABLE_CRM360_INTEGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ Já foram criados e commitados no GitHub:
- `src/components/inbox/contact-details/ExternalContact360Panel.tsx` — painel 360° no ContactDetails
- `src/components/contacts/AdvancedCRMSearch.tsx` — busca avançada com filtros

Os **environment secrets** necessários já estão no `.env`:
Os **environment secrets** necessários devem ser configurados no `.env`:
```
VITE_EXTERNAL_SUPABASE_URL="https://pgxfvjmuubtbowutlide.supabase.co"
VITE_EXTERNAL_SUPABASE_ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBneGZ2am11dWJ0Ym93dXRsaWRlIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzAxMjcwMTIsImV4cCI6MjA4NTcwMzAxMn0.sW9N_LChqwVNUvMmQWXx87Vhs3eoTI2OKg2TT_Cg4V0"
VITE_EXTERNAL_SUPABASE_ANON_KEY="<YOUR_SUPABASE_ANON_KEY>"
```

**IMPORTANTE:** Esses secrets também precisam ser adicionados no painel do Lovable em Settings → Environment Variables.
Expand Down
6 changes: 3 additions & 3 deletions docs/architecture/SELF-HOSTED-DATABASE-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ O ZAPP Web usa **dois bancos de dados Supabase**:

```
URL: https://supabase.atomicabr.com.br
Anon Key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJyb2xlIjogImFub24iLAogICJpc3MiOiAic3VwYWJhc2UiLAogICJpYXQiOiAxNzE1MDUwODAwLAogICJleHAiOiAxODcyODE3MjAwCn0.rvamc0XHuSCYB1glBwOCCxgfd9yxWVYLnhFzg5-7TRk
Anon Key: <configure via VITE_EXTERNAL_SUPABASE_ANON_KEY no .env>
```

Estas credenciais já estão hardcoded no arquivo:
`src/integrations/supabase/externalClient.ts`
Estas credenciais devem ser configuradas via variáveis de ambiente no arquivo `.env`:
`VITE_EXTERNAL_SUPABASE_ANON_KEY=<seu-anon-key>`

---

Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@
"xlsx": "^0.18.5",
"zod": "^4.4.3"
},
"lint-staged": {
"*.{ts,tsx}": ["eslint --no-warn-ignored"]
},
"devDependencies": {
"@eslint/js": "^9.32.0",
"@playwright/test": "^1.59.1",
Expand All @@ -123,6 +126,7 @@
"globals": "^15.15.0",
"happy-dom": "^20.9.0",
"husky": "^9.1.7",
"lint-staged": "^15.5.2",
"playwright": "^1.59.1",
"postcss": "^8.5.6",
"prettier": "^3.8.3",
Expand Down
105 changes: 105 additions & 0 deletions scripts/verify_rls_compliance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env bun
/**
* RLS Compliance Script — verifica se todas as tabelas públicas têm RLS habilitado.
* Lê os arquivos de migração SQL e gera um relatório Markdown.
*
* Usage: bun scripts/verify_rls_compliance.ts > rls-compliance-report.md
*/

import { readFileSync, readdirSync, statSync } from 'fs';
import { join } from 'path';

const MIGRATIONS_DIR = join(process.cwd(), 'supabase', 'migrations');
const LEGACY_MIGRATIONS_DIR = join(process.cwd(), 'supabase', 'migrations-from-lovable');

function readSqlFiles(dir: string): string {
let combined = '';
try {
const entries = readdirSync(dir);
for (const entry of entries) {
const fullPath = join(dir, entry);
if (statSync(fullPath).isFile() && entry.endsWith('.sql')) {
combined += '\n' + readFileSync(fullPath, 'utf-8');
}
}
} catch {
// directory may not exist
}
return combined;
}

function extractTables(sql: string): Set<string> {
const tables = new Set<string>();
const re = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?public\.([a-zA-Z_][a-zA-Z0-9_]*)/gi;
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 Include quoted identifiers when extracting table names

The table extractor regex only matches unquoted public.table names, so migrations that use quoted identifiers (e.g. CREATE TABLE IF NOT EXISTS public."channel_connections_safe" in supabase/migrations/20260502_create_missing_tables.sql) are skipped by the compliance audit. That undercounts total tables and can hide missing-RLS violations for any quoted table definition.

Useful? React with 👍 / 👎.

let m: RegExpExecArray | null;
while ((m = re.exec(sql)) !== null) {
tables.add(m[1].toLowerCase());
}
return tables;
}

function extractRlsEnabled(sql: string): Set<string> {
const enabled = new Set<string>();
const re = /ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:ONLY\s+)?(?:public\.)?([a-zA-Z_][a-zA-Z0-9_]*)\s+ENABLE\s+ROW\s+LEVEL\s+SECURITY/gi;
let m: RegExpExecArray | null;
while ((m = re.exec(sql)) !== null) {
enabled.add(m[1].toLowerCase());
}
return enabled;
}

const allSql = readSqlFiles(MIGRATIONS_DIR) + readSqlFiles(LEGACY_MIGRATIONS_DIR);

const tables = extractTables(allSql);
const rlsEnabled = extractRlsEnabled(allSql);

const compliant: string[] = [];
const violations: string[] = [];

for (const table of [...tables].sort()) {
if (rlsEnabled.has(table)) {
compliant.push(table);
} else {
violations.push(table);
}
}

const now = new Date().toISOString();
const status = violations.length === 0 ? '✅ COMPLIANT' : `⚠️ ${violations.length} VIOLATION(S)`;

const report = `# RLS Compliance Report

**Generated:** ${now}
**Status:** ${status}

## Summary

| Metric | Count |
|--------|-------|
| Total tables | ${tables.size} |
| RLS enabled | ${compliant.length} |
| Missing RLS | ${violations.length} |

## Tables Missing RLS (${violations.length})

${violations.length === 0
? '_All tables have Row Level Security enabled._'
: violations.map(t => `- \`public.${t}\``).join('\n')
}

## Tables with RLS Enabled (${compliant.length})

<details>
<summary>Click to expand</summary>

${compliant.map(t => `- \`public.${t}\``).join('\n')}

</details>
`;

process.stdout.write(report);

if (violations.length > 0) {
process.stderr.write(`\nRLS compliance check FAILED: ${violations.length} table(s) missing RLS.\n`);
process.exit(1);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
-- Enable RLS on tables that were missing Row Level Security policies.
-- All 8 tables identified by scripts/verify_rls_compliance.ts.

ALTER TABLE IF EXISTS public.avatars ENABLE ROW LEVEL SECURITY;
ALTER TABLE IF EXISTS public.conversation_summaries ENABLE ROW LEVEL SECURITY;
ALTER TABLE IF EXISTS public.email_templates ENABLE ROW LEVEL SECURITY;
ALTER TABLE IF EXISTS public.message_queue ENABLE ROW LEVEL SECURITY;
ALTER TABLE IF EXISTS public.messages_whatsapp ENABLE ROW LEVEL SECURITY;
ALTER TABLE IF EXISTS public.salespeople ENABLE ROW LEVEL SECURITY;
ALTER TABLE IF EXISTS public.system_logs ENABLE ROW LEVEL SECURITY;
ALTER TABLE IF EXISTS public.vault_healthcheck_log ENABLE ROW LEVEL SECURITY;

DO $$ DECLARE t TEXT;
BEGIN
FOR t IN SELECT unnest(ARRAY[
'avatars',
'conversation_summaries',
'email_templates',
'message_queue',
'messages_whatsapp',
'salespeople',
'system_logs',
'vault_healthcheck_log'
]) LOOP
BEGIN
EXECUTE format(
'CREATE POLICY auth_rw ON public.%I FOR ALL TO authenticated USING (true) WITH CHECK (true)',
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot May 19, 2026

Choose a reason for hiding this comment

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

P0: This policy grants unrestricted CRUD on every row to all authenticated users (USING (true) / WITH CHECK (true)), creating cross-user data exposure/tampering risk.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/migrations/20260519000000_enable_rls_missing_tables.sql, line 27:

<comment>This policy grants unrestricted CRUD on every row to all authenticated users (`USING (true)` / `WITH CHECK (true)`), creating cross-user data exposure/tampering risk.</comment>

<file context>
@@ -0,0 +1,40 @@
+  ]) LOOP
+    BEGIN
+      EXECUTE format(
+        'CREATE POLICY auth_rw ON public.%I FOR ALL TO authenticated USING (true) WITH CHECK (true)',
+        t
+      );
</file context>
Fix with Cubic

Comment on lines +26 to +27
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 Handle missing tables before creating RLS policies

This migration uses ALTER TABLE IF EXISTS for the eight targets, but the subsequent CREATE POLICY statements run unconditionally via dynamic SQL and only catch duplicate_object. In any environment where one of those tables is absent (e.g., schema drift, partial backfill, or branch-specific DB), CREATE POLICY ... ON public.%I raises undefined_table and aborts the migration, which can block deployments; add an existence guard (or catch undefined_table) around policy creation too.

Useful? React with 👍 / 👎.

t
);
EXCEPTION WHEN duplicate_object THEN NULL;
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot May 19, 2026

Choose a reason for hiding this comment

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

P2: This migration can still fail on environments missing one of these tables because CREATE POLICY doesn't handle undefined_table.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/migrations/20260519000000_enable_rls_missing_tables.sql, line 30:

<comment>This migration can still fail on environments missing one of these tables because `CREATE POLICY` doesn't handle `undefined_table`.</comment>

<file context>
@@ -0,0 +1,40 @@
+        'CREATE POLICY auth_rw ON public.%I FOR ALL TO authenticated USING (true) WITH CHECK (true)',
+        t
+      );
+    EXCEPTION WHEN duplicate_object THEN NULL;
+    END;
+    BEGIN
</file context>
Fix with Cubic

END;
BEGIN
EXECUTE format(
'CREATE POLICY svc_rw ON public.%I FOR ALL TO service_role USING (true) WITH CHECK (true)',
t
);
EXCEPTION WHEN duplicate_object THEN NULL;
END;
END LOOP;
END $$;
Loading