Skip to content

[INCORPORATED] Fix/credentials audit burn down sprint1-4#458

Merged
adm01-debug merged 10 commits into
mainfrom
fix/credentials-audit-burn-down-sprint1-4
May 26, 2026
Merged

[INCORPORATED] Fix/credentials audit burn down sprint1-4#458
adm01-debug merged 10 commits into
mainfrom
fix/credentials-audit-burn-down-sprint1-4

Conversation

@adm01-debug
Copy link
Copy Markdown
Owner

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

Fechado: conteúdo incorporado ao main via commits diretos no VPS durante resolução de conflitos. As correções de credentials vault (expert-chat, sync-external-db, webhook-dispatcher, simulation-orchestrator, send-scheduled-reports, secure-upload, product-webhook) e o baseline atualizado (issues 28→6→0) já estão em origin/main. Diff atual entre main e esse branch: apenas arquivos modificados pelo Lovable após a data do PR, que não fazem parte desse escopo.

Copilot AI review requested due to automatic review settings May 26, 2026 14:13
@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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

Walkthrough

Este PR migra a obtenção de credenciais em sete edge functions, movendo de Deno.env.get() direto para um vault centralizado via getCredential. Product-webhook adiciona carregamento por requisição e configuração de tolerância de nonce. O audit baseline reflete a redução de 28 para 6 credenciais rastreadas.

Changes

Migração para Credential Vault

Layer / File(s) Summary
Atualização de baseline de auditoria
.audit-credentials-baseline.json
Baseline refatorado de schema v2-counts com signatures para lista de issues explícitos; issue_count reduzido de 28 para 6, refletindo credenciais agora centralizadas em vault.
Product webhook: migração e configuração por requisição
supabase/functions/product-webhook/index.ts
isReplayNonce passa a receber toleranceSec como parâmetro; handler carrega webhookSecret e configuredWebhookToleranceSec por requisição via getCredential com clamp entre DEFAULT_WEBHOOK_TOLERANCE_SEC e MAX_WEBHOOK_TOLERANCE_SEC; novo PRODUCT_WEBHOOK_BATCH_SIZE com validação [100, 500], padrão 200.
Expert chat: credenciais externas via vault
supabase/functions/expert-chat/index.ts
Rotas principal e fallback migram de Deno.env para getCredential obtendo EXT_URL/EXT_KEY e EXT_URL2/EXT_KEY2 respectivamente, com anotações ssot-bypass.
Migrações simples em seis edge functions
supabase/functions/secure-upload/index.ts, send-scheduled-reports/index.ts, simulation-orchestrator/index.ts, sync-external-db/index.ts, webhook-dispatcher/index.ts
Cada função migra uma ou mais credenciais específicas (VIRUSTOTAL_API_KEY, RESEND_API_KEY, N8N_PRODUCT_WEBHOOK_SECRET, EXTERNAL_PROMOBRIND_*, WEBHOOK_DISPATCHER_SECRET) de Deno.env.get() para await getCredential(), mantendo validações e fluxos de erro equivalentes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • adm01-debug/promo-gifts-v4#109: Ambos refletem migração de secrets de Deno.env para DB-first credential vault centralizado via getCredential / resolveCredential.

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive O título é genérico e vago, referenciando apenas 'sprint1 4' e 'burn down' sem explicar a mudança principal: migração de credenciais para vault. Use um título mais descritivo, como 'Migrate hardcoded env vars to credential vault across edge functions' para deixar claro o objetivo.
✅ Passed checks (3 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/credentials-audit-burn-down-sprint1-4

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

@supabase
Copy link
Copy Markdown

supabase Bot commented May 26, 2026

Updates to Preview Branch (fix/credentials-audit-burn-down-sprint1-4) ↗︎

Deployments Status Updated
Database Tue, 26 May 2026 16:26:39 UTC
Services Tue, 26 May 2026 16:26:39 UTC
APIs Tue, 26 May 2026 16:26:39 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Tue, 26 May 2026 16:26:43 UTC
Migrations Tue, 26 May 2026 16:26:45 UTC
Seeding ⏸️ Tue, 26 May 2026 16:26:31 UTC
Edge Functions ⏸️ Tue, 26 May 2026 16:26:31 UTC

❌ Branch Error • Tue, 26 May 2026 16:26:46 UTC

ERROR: cannot change return type of existing function (SQLSTATE 42P13)
At statement: 10
CREATE OR REPLACE FUNCTION public.fn_run_and_persist_smoke_tests()
RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path TO 'public'
AS $function$
DECLARE
  v_ran_at timestamptz := now();
BEGIN
  IF NOT public.is_admin_or_above((SELECT auth.uid())) THEN
    RAISE EXCEPTION 'not authorized';
  END IF;

  INSERT INTO public.smoke_test_runs (
    ran_at,
    test_name,
    test_category,
    result,
    details,
    duration_ms
  )
  SELECT
    v_ran_at,
    test_name,
    test_category,
    result,
    details,
    duration_ms
  FROM public.fn_run_smoke_tests();
END;
$function$

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

Copy link
Copy Markdown
Contributor

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

Este PR migra duas Edge Functions (materials-api e health-check) para resolver credenciais do Supabase externo (Promobrind) via credential vault (getCredential), reduzindo acessos diretos a Deno.env e atendendo itens da auditoria de credenciais (SSOT-bypass / leituras em module-scope).

Changes:

  • materials-api: troca leitura de EXTERNAL_SUPABASE_* por getCredential('EXTERNAL_PROMOBRIND_*') e mantém fallback de RPC→query.
  • health-check: troca leitura de EXTERNAL_SUPABASE_* por getCredential('EXTERNAL_PROMOBRIND_*') no checker do banco externo.
  • Ajustes de formatação/compactação de trechos (sem mudança intencional de comportamento).

Reviewed changes

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

File Description
supabase/functions/materials-api/index.ts Usa credential vault para conectar no Supabase externo e simplifica trechos do handler/respostas.
supabase/functions/health-check/index.ts Usa credential vault no checker do DB externo e mantém agregação do status de health.

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

const externalKey = await getCredential('EXTERNAL_PROMOBRIND_SERVICE_ROLE_KEY');

if (!externalUrl || !externalKey) {
console.warn('[materials-api] EXTERNAL_SUPABASE_URL/KEY not configured — returning empty payload');
Comment on lines +213 to +216
const errorMessage = error instanceof Error ? error.message : (error?.message || error?.error_description || error?.hint || error?.details || JSON.stringify(error) || 'Erro desconhecido');
const errorCode = error?.code ?? null;
console.error('Materials API error:', errorMessage, 'code:', errorCode, 'raw:', JSON.stringify(error));

return new Response(
JSON.stringify({ error: errorMessage, code: errorCode }),
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
return new Response(JSON.stringify({ error: errorMessage, code: errorCode }), { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } });
Comment on lines 1 to +5
import { createClient, SupabaseClient } from "https://esm.sh/@supabase/supabase-js@2.49.1";
import { handleCorsPreflight } from "../_shared/cors.ts";
import { getOrCreateRequestId } from "../_shared/request-id.ts";
import { createStructuredLogger } from "../_shared/structured-logger.ts";
import { getCredential } from "../_shared/credentials.ts";
Comment on lines 57 to 60
// fix: ssot-bypass + module-scope-credential-read — use credential vault
const url = await getCredential('EXTERNAL_PROMOBRIND_URL');
const key = await getCredential('EXTERNAL_PROMOBRIND_SERVICE_ROLE_KEY');

Comment on lines +47 to +49
// fix: ssot-bypass — use credential vault (aliases EXTERNAL_SUPABASE_URL / SERVICE_ROLE_KEY)
const externalUrl = await getCredential('EXTERNAL_PROMOBRIND_URL');
const externalKey = await getCredential('EXTERNAL_PROMOBRIND_SERVICE_ROLE_KEY');
@vercel
Copy link
Copy Markdown

vercel Bot commented May 26, 2026

Deployment failed with the following error:

Invalid vercel.json file provided

@vercel
Copy link
Copy Markdown

vercel Bot commented May 26, 2026

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

Project Deployment Actions Updated (UTC)
we-dream-big Error Error May 26, 2026 4:26pm

Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
supabase/functions/expert-chat/index.ts (1)

1001-1003: ⚡ Quick win

Evite lookup duplicado do vault no fallback

As credenciais já são carregadas e validadas no bloco principal; no fallback elas são buscadas novamente sem necessidade. Isso adiciona latência e um ponto extra de falha por requisição. Reutilize EXT_URL/EXT_KEY já obtidos.

💡 Diff sugerido
-    // fix: ssot-bypass — credential vault
-    const EXT_URL = await getCredential('EXTERNAL_PROMOBRIND_URL');
-    const EXT_KEY = await getCredential('EXTERNAL_PROMOBRIND_SERVICE_ROLE_KEY');
+    // fix: ssot-bypass — credential vault
+    const EXT_URL = await getCredential('EXTERNAL_PROMOBRIND_URL');
+    const EXT_KEY = await getCredential('EXTERNAL_PROMOBRIND_SERVICE_ROLE_KEY');

     if (!EXT_URL || !EXT_KEY) {
-      console.error('External DB env vars not set — cannot fetch products');
+      console.error('External DB credentials not set — cannot fetch products');
     } else if (
       expansion.intent === 'product_search' ||
       expansion.intent === 'proposal' ||
       expansion.searchTerms.length > 0 ||
       fallbackTerms.length > 0
     ) {
       const extClient = createClient(EXT_URL, EXT_KEY);
       ...
     } else {
-      // fix: ssot-bypass — credential vault
-      const EXT_URL2 = await getCredential('EXTERNAL_PROMOBRIND_URL');
-      const EXT_KEY2 = await getCredential('EXTERNAL_PROMOBRIND_SERVICE_ROLE_KEY');
-      if (EXT_URL2 && EXT_KEY2) {
-        const extClient = createClient(EXT_URL2, EXT_KEY2);
+      const extClient = createClient(EXT_URL, EXT_KEY);
         let q = extClient
           .from('products')
           .select(
             'id, name, sku, sale_price, primary_image_url, category_id, description, brand, stock_quantity',
           )
           .eq('active', true);
...
-      }
     }

Also applies to: 1283-1285

🤖 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/functions/expert-chat/index.ts` around lines 1001 - 1003, No bloco
de fallback está sendo feito um lookup duplicado ao chamar
getCredential('EXTERNAL_PROMOBRIND_URL') e
getCredential('EXTERNAL_PROMOBRIND_SERVICE_ROLE_KEY') novamente; em vez disso
reutilize as variáveis já carregadas EXT_URL e EXT_KEY (definidas no escopo
principal) dentro do fallback para evitar latência e pontos extras de falha;
localize o trecho que chama getCredential na seção de fallback e substitua essas
chamadas por referências a EXT_URL e EXT_KEY (mantendo a validação existente
caso necessário).
🤖 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 `@supabase/functions/simulation-orchestrator/index.ts`:
- Around line 63-64: Remove the hardcoded fallback by deleting the "??
'sim-secret'" default; call getCredential("N8N_PRODUCT_WEBHOOK_SECRET") (or
Deno.env.get("N8N_PRODUCT_WEBHOOK_SECRET") per guidelines) and if it returns
undefined, fail closed by returning/throwing a 503 response (log the missing
secret) instead of using a predictable secret; update the n8nSecret usage sites
to assume a present secret after this check (ref: n8nSecret variable and
getCredential call).

---

Nitpick comments:
In `@supabase/functions/expert-chat/index.ts`:
- Around line 1001-1003: No bloco de fallback está sendo feito um lookup
duplicado ao chamar getCredential('EXTERNAL_PROMOBRIND_URL') e
getCredential('EXTERNAL_PROMOBRIND_SERVICE_ROLE_KEY') novamente; em vez disso
reutilize as variáveis já carregadas EXT_URL e EXT_KEY (definidas no escopo
principal) dentro do fallback para evitar latência e pontos extras de falha;
localize o trecho que chama getCredential na seção de fallback e substitua essas
chamadas por referências a EXT_URL e EXT_KEY (mantendo a validação existente
caso necessário).
🪄 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: 962238b8-d424-496f-868d-91b375a540d5

📥 Commits

Reviewing files that changed from the base of the PR and between 33a39e4 and 59f88c0.

📒 Files selected for processing (8)
  • .audit-credentials-baseline.json
  • supabase/functions/expert-chat/index.ts
  • supabase/functions/product-webhook/index.ts
  • supabase/functions/secure-upload/index.ts
  • supabase/functions/send-scheduled-reports/index.ts
  • supabase/functions/simulation-orchestrator/index.ts
  • supabase/functions/sync-external-db/index.ts
  • supabase/functions/webhook-dispatcher/index.ts

Comment on lines +63 to +64
// fix: ssot-bypass — credential vault
const n8nSecret = await getCredential("N8N_PRODUCT_WEBHOOK_SECRET") ?? "sim-secret";
Copy link
Copy Markdown

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

Remover fallback de segredo hardcoded em Line 64.

?? "sim-secret" introduz segredo previsível quando o vault falha/ausenta credencial, enfraquecendo a proteção do webhook. Falhe fechado com 503 em vez de usar default secreto.

💡 Patch sugerido
-    const n8nSecret = await getCredential("N8N_PRODUCT_WEBHOOK_SECRET") ?? "sim-secret";
+    const n8nSecret = await getCredential("N8N_PRODUCT_WEBHOOK_SECRET");
+    if (!n8nSecret) {
+      return new Response(JSON.stringify({ error: "service_misconfigured" }), {
+        status: 503,
+        headers: { ...corsHeaders, ...responseHeaders, "Content-Type": "application/json" },
+      });
+    }

As per coding guidelines "Secrets sempre via Deno.env.get(), NUNCA hardcoded".

🤖 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/functions/simulation-orchestrator/index.ts` around lines 63 - 64,
Remove the hardcoded fallback by deleting the "?? 'sim-secret'" default; call
getCredential("N8N_PRODUCT_WEBHOOK_SECRET") (or
Deno.env.get("N8N_PRODUCT_WEBHOOK_SECRET") per guidelines) and if it returns
undefined, fail closed by returning/throwing a 503 response (log the missing
secret) instead of using a predictable secret; update the n8nSecret usage sites
to assume a present secret after this check (ref: n8nSecret variable and
getCredential call).

TIPROMO added 10 commits May 26, 2026 13:25
…{}) + regenerate from clean state

Previous baseline used deprecated v2-counts schema with signatures{} object.
Audit script expects issues[] array format. Regenerated from actual scan:
28 (original) → 6 remaining (categories-api, cleanup-novelties, external-db-inspect).
cnpj-lookup already fixed by another PR in main.
- useGlobalSearch.ts: remove type SimpleQueryBuilder orphan (ficou do PR #380 mas untypedFrom do main não o usa)
- MockupGenerator.tsx: remove import Badge não usado
- MockupGenerator.tsx: renomeia summary → _summary (definida mas não usada no JSX)

ESLint baseline gate: ✅ zero regressões (drift positivo: -19 erros)
@adm01-debug adm01-debug force-pushed the fix/credentials-audit-burn-down-sprint1-4 branch from 110c0a2 to 0d42b1a Compare May 26, 2026 16:25
@adm01-debug adm01-debug merged commit dada517 into main May 26, 2026
39 of 47 checks passed
@adm01-debug adm01-debug changed the title Fix/credentials audit burn down sprint1 4 [INCORPORATED] Fix/credentials audit burn down sprint1-4 May 26, 2026
@adm01-debug adm01-debug deleted the fix/credentials-audit-burn-down-sprint1-4 branch May 29, 2026 13:37
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