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
8 changes: 6 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ VITE_SUPABASE_URL=https://doufsxqlfjyuvxuezpln.supabase.co
# ID do projeto Supabase (ex: abcdefgh)
VITE_SUPABASE_PROJECT_ID=doufsxqlfjyuvxuezpln

# Chave PUBLISHABLE (anon key) — segura no client
# Chave anon (pública) — segura no client-side
# ⚠️ NUNCA coloque a key real aqui — este arquivo é público no GitHub.
# Obtenha a key em: Dashboard → Settings → API → Project API keys → anon public
VITE_SUPABASE_PUBLISHABLE_KEY=sb_publishable_<your-anon-key-here>
#
# Suporte a DOIS nomes de variável (compatibilidade legada + padrão Supabase):
VITE_SUPABASE_ANON_KEY=<your-anon-key-here>
# Alias legado (também suportado pelo código, mas prefira ANON_KEY acima):
# VITE_SUPABASE_PUBLISHABLE_KEY=<your-anon-key-here>

# ----------------------------------------------------------------------------
# OBSERVABILIDADE — GlitchTip / Sentry (opcional, recomendado em produção)
Expand Down
122 changes: 121 additions & 1 deletion .github/workflows/quality-gate.yml
Original file line number Diff line number Diff line change
@@ -1 +1,121 @@
bmFtZTogUXVhbGl0eSBHYXRlCgpvbjoKICBwdWxsX3JlcXVlc3Q6CiAgICBicmFuY2hlczoKICAgICAgLSBtYWluCiAgcHVzaDoKICAgIGJyYW5jaGVzOgogICAgICAtIG1haW4KCmpvYnM6CiAgcXVhbGl0eS1nYXRlOgogICAgbmFtZTogVHlwZVNjcmlwdCArIEVTTGludCBHYXRlCiAgICBydW5zLW9uOiB1YnVudHUtbGF0ZXN0CiAgICB0aW1lb3V0LW1pbnV0ZXM6IDE1CgogICAgc3RlcHM6CiAgICAgIC0gbmFtZTogQ2hlY2tvdXQKICAgICAgICB1c2VzOiBhY3Rpb25zL2NoZWNrb3V0QHY0CgogICAgICAtIG5hbWU6IFNldHVwIE5vZGUKICAgICAgICB1c2VzOiBhY3Rpb25zL3NldHVwLW5vZGVAdjQKICAgICAgICB3aXRoOgogICAgICAgICAgbm9kZS12ZXJzaW9uLWZpbGU6IC5udm1yYwogICAgICAgICAgY2FjaGU6IG5wbQoKICAgICAgLSBuYW1lOiBJbnN0YWxsIGRlcGVuZGVuY2llcwogICAgICAgIHJ1bjogbnBtIGNpIC0tcHJlZmVyLW9mZmxpbmUKCiAgICAgICMgR0FURSAxOiBUeXBlU2NyaXB0IC0gemVybyByZWdyZXNzaW9ucyB2cyBiYXNlbGluZQogICAgICAtIG5hbWU6IFR5cGVTY3JpcHQgZ2F0ZSAoemVybyByZWdyZXNzaW9ucykKICAgICAgICBydW46IG5vZGUgc2NyaXB0cy9jaGVjay10c2MtYmFzZWxpbmUubWpzCiAgICAgICAgZW52OgogICAgICAgICAgVklURV9TVVBBQKFTRV9VUkw6ICRkdW1teQogICAgICAgICAgVklURV9TVVBBQkFTRV9BTk9OX0tFWTogJGR1bW15CgogICAgICAjIEdBVEUgMjogRVNMaW50IC0gemVybyByZWdyZXNzaW9ucyB2cyBiYXNlbGluZQogICAgICAtIG5hbWU6IEVTTGludCBnYXRlICh6ZXJvIHJlZ3Jlc3Npb25zKQogICAgICAgIHJ1bjogbnBtIHJ1biBsaW50OmJhc2VsaW5lCiAgICAgICAgZW52OgogICAgICAgICAgVklURV9TVVBBQKFTRV9VUkw6ICRkdW1teQogICAgICAgICAgVklURV9TVVBBQkFTRV9BTk9OX0tFWTogJGR1bW15CgogICAgICAjIEdBVEUgMzogQnVpbGQgKHZlcmlmeSBubyBidWlsZCBicmVha2FnZSkKICAgICAgLSBuYW1lOiBCdWlsZCBjaGVjawogICAgICAgIHJ1bjogbnBtIHJ1biBidWlsZAogICAgICAgIGVudjoKICAgICAgICAgIFZJVEVfU1VQQUJBU0VfVVJMOiAkZHVtbXkKICAgICAgICAgIFZJVEVfU1VQQUJBU0VfQU5PTl9LRVk6ICRkdW1teQoKICAjIEdBVEUgNDogVml0ZXN0IHVuaXQgdGVzdHMgKGZhc3QgLSBubyBuZXR3b3JrKQogIHVuaXQtdGVzdHM6CiAgICBuYW1lOiBWaXRlc3QgVW5pdCBUZXN0cwogICAgcnVucy1vbjogdWJ1bnR1LWxhdGVzdAogICAgdGltZW91dC1taW51dGVzOiAxMAoKICAgIHN0ZXBzOgogICAgICAtIG5hbWU6IENoZWNrb3V0CiAgICAgICAgdXNlczogYWN0aW9ucy9jaGVja291dEB2NAoKICAgICAgLSBuYW1lOiBTZXR1cCBOb2RlCiAgICAgICAgdXNlczogYWN0aW9ucy9zZXR1cC1ub2RlQHY0CiAgICAgICAgd2l0aDoKICAgICAgICAgIG5vZGUtdmVyc2lvbi1maWxlOiAubnZtcmMKICAgICAgICAgIGNhY2hlOiBucG0KCiAgICAgIC0gbmFtZTogSW5zdGFsbCBkZXBlbmRlbmNpZXMKICAgICAgICBydW46IG5wbSBjaSAtLXByZWZlci1vZmZsaW5lCgogICAgICAtIG5hbWU6IFJ1biB1bml0IHRlc3RzCiAgICAgICAgcnVuOiBucG0gcnVuIHRlc3Q6Y2ktY29yZQogICAgICAgIGVudjoKICAgICAgICAgIFZJVEVfU1VQQUJBU0VfVVJMOiAkZHVtbXkKICAgICAgICAgIFZJVEVfU1VQQUJBU0VfQU5PTl9LRVk6ICRkdW1teQo=
name: Quality Gate

on:
pull_request:
branches:
- main
push:
branches:
- main

jobs:
quality-gate:
name: TypeScript + ESLint Gate
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm

- name: Install dependencies
run: npm ci --prefer-offline

# GATE 1: TypeScript - zero regressions vs baseline
- name: TypeScript gate (zero regressions)
run: node scripts/check-tsc-baseline.mjs
env:
VITE_SUPABASE_URL: ${{ vars.VITE_SUPABASE_URL || 'https://doufsxqlfjyuvxuezpln.supabase.co' }}
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY || 'dummy-key-for-typecheck' }}

# GATE 2: ESLint - zero regressions vs baseline
- name: ESLint gate (zero regressions)
run: npm run lint:baseline
env:
VITE_SUPABASE_URL: ${{ vars.VITE_SUPABASE_URL || 'https://doufsxqlfjyuvxuezpln.supabase.co' }}
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY || 'dummy-key-for-typecheck' }}

# GATE 3: Build (verify no build breakage)
- name: Build check
run: npm run build
env:
VITE_SUPABASE_URL: ${{ vars.VITE_SUPABASE_URL || 'https://doufsxqlfjyuvxuezpln.supabase.co' }}
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY || 'dummy-key-for-typecheck' }}

# GATE 4: Vitest unit tests (fast - no network)
unit-tests:
name: Vitest Unit Tests
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm

- name: Install dependencies
run: npm ci --prefer-offline

- name: Run unit tests
run: npm run test:ci-core
env:
VITE_SUPABASE_URL: ${{ vars.VITE_SUPABASE_URL || 'https://doufsxqlfjyuvxuezpln.supabase.co' }}
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY || 'dummy-key-for-typecheck' }}

# GATE 5: Supabase types sync check (detecta type drift)
supabase-types:
name: Supabase Types Drift Check
runs-on: ubuntu-latest
timeout-minutes: 10
# Roda apenas em PRs para não bloquear push no main sem secrets
if: github.event_name == 'pull_request'

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm

- name: Install dependencies
run: npm ci --prefer-offline

- name: Install Supabase CLI
uses: supabase/setup-cli@v1
with:
version: latest

- name: Generate Supabase types
id: gen_types
run: |
npx supabase gen types typescript \
--project-id doufsxqlfjyuvxuezpln \
> /tmp/supabase-types-fresh.ts
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
continue-on-error: true

- name: Check for type drift
if: steps.gen_types.outcome == 'success'
run: |
if ! diff -q src/integrations/supabase/types.ts /tmp/supabase-types-fresh.ts > /dev/null 2>&1; then
echo "::warning::Supabase types are out of sync with the database schema!"
echo "::warning::Run: npx supabase gen types typescript --project-id doufsxqlfjyuvxuezpln > src/integrations/supabase/types.ts"
diff src/integrations/supabase/types.ts /tmp/supabase-types-fresh.ts | head -50
else
echo "Types are in sync ✅"
fi
Comment on lines +102 to +121
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
-- ============================================================
-- Migration: Corrige policy perigosa na tabela ai_description_queue
-- Auditoria 2026-06-02 — RISCO-1 identificado
--
-- Problema: Policy "ai_queue_service_all" com USING(true) permite
-- que QUALQUER usuário autenticado leia/escreva/delete filas de IA,
-- criando risco de IDOR (Insecure Direct Object Reference).
--
-- Solução: Substituir por políticas granulares de mínimo privilégio:
-- - SELECT: owner OR admin
-- - INSERT: owner (auth.uid() = requested_by)
-- - UPDATE: somente admins
-- - DELETE: somente admins
-- ============================================================

-- Remove a policy permissiva original
DROP POLICY IF EXISTS "ai_queue_service_all" ON ai_description_queue;

-- SELECT: usuário vê apenas suas próprias filas, admins veem tudo
CREATE POLICY "ai_queue_read_own_or_admin"
ON ai_description_queue
FOR SELECT
USING (
auth.uid() = requested_by
OR is_admin_or_above(auth.uid())
);
Comment on lines +19 to +26

-- INSERT: usuário pode inserir somente como ele mesmo
CREATE POLICY "ai_queue_insert_own"
ON ai_description_queue
FOR INSERT
WITH CHECK (
auth.uid() = requested_by
);
Comment on lines +28 to +34

-- UPDATE: somente admins podem atualizar status de fila
CREATE POLICY "ai_queue_update_admin_only"
ON ai_description_queue
FOR UPDATE
USING (
is_admin_or_above(auth.uid())
)
WITH CHECK (
is_admin_or_above(auth.uid())
);
Comment on lines +36 to +45

-- DELETE: somente admins podem remover entradas de fila
CREATE POLICY "ai_queue_delete_admin_only"
ON ai_description_queue
FOR DELETE
USING (
is_admin_or_above(auth.uid())
);
Comment on lines +47 to +53

-- Garantir RLS habilitado (já deve estar, mas garantia)
ALTER TABLE ai_description_queue ENABLE ROW LEVEL SECURITY;
Loading