-
Notifications
You must be signed in to change notification settings - Fork 0
Lovable sync 1777298482 #84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
68853ef
cd97caa
8067b0b
8dae703
e5d862f
3b02a0a
31f5919
7bb5bd9
6c79bb8
905676f
035ba00
93efe74
335b325
6284f98
d795848
e9a0d95
d1b03e4
0b652a3
f07a4c5
a1e404c
a44f22b
1f9e1a1
97cf441
2f3c3c6
b59105f
bd23e56
21a24a6
942c20f
6321605
548aa73
5217c37
23c78eb
69b53fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {"$schema":"./baseline.schema.json","generatedAt":null,"threshold":"high","knownAdvisories":[],"notes":"Atualize com UPDATE_BASELINE=1 node scripts/check-npm-audit.mjs após revisar cada CVE."} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| -- ============================================================================ | ||
| -- Webhook Alerts Spec — Detection Queries (read-only) | ||
| -- ---------------------------------------------------------------------------- | ||
| -- SSOT das queries que a edge function `webhook-alerts-monitor` executa para | ||
| -- decidir se dispara um alerta no Sentry. Nenhuma destas queries faz INSERT/ | ||
| -- UPDATE — todas leem `public.webhook_delivery_metrics` (schema definido em | ||
| -- supabase/migrations/20260427122230_*.sql). | ||
| -- | ||
| -- Colunas usadas: | ||
| -- occurred_at timestamptz — quando a tentativa ocorreu | ||
| -- source text — ex: 'bitrix', 'product-webhook', 'stripe' | ||
| -- direction text — 'inbound' | 'outbound' | ||
| -- http_status int — código HTTP (NULL para erro de transporte) | ||
| -- success boolean — sucesso final (após retries) | ||
| -- request_id text — correlation id | ||
| -- | ||
| -- Janelas e thresholds (perfil CONSERVADOR — confirmado pelo usuário): | ||
| -- * delivery_failure_total : >=3 falhas CONSECUTIVAS por (source,direction) | ||
| -- * spike_5xx : >=5 respostas 5xx em 5min OU >20% das requests | ||
| -- (mín 10 amostras na janela) | ||
| -- * spike_4xx : >40% de respostas 4xx em 5min (mín 10 amostras) | ||
| -- | ||
| -- Cada query devolve UMA linha por (source,direction) que ATINGE o threshold. | ||
| -- A edge monitor mapeia cada linha para um evento Sentry com tags: | ||
| -- alert=<id>, source=<source>, direction=<direction>, severity=warning|error | ||
| -- ============================================================================ | ||
|
|
||
| -- :window_minutes -> default 5 | ||
| -- :min_samples -> default 10 | ||
| -- :rate_5xx -> default 0.20 | ||
| -- :rate_4xx -> default 0.40 | ||
| -- :abs_5xx -> default 5 | ||
| -- :consecutive -> default 3 | ||
|
|
||
| -- ---------------------------------------------------------------------------- | ||
| -- 1) delivery_failure_total | ||
| -- >= N falhas CONSECUTIVAS (mais recentes) por (source,direction). | ||
| -- ---------------------------------------------------------------------------- | ||
| with recent as ( | ||
| select | ||
| source, | ||
| direction, | ||
| success, | ||
| occurred_at, | ||
| row_number() over (partition by source, direction order by occurred_at desc) as rn | ||
| from public.webhook_delivery_metrics | ||
| where occurred_at > now() - (:window_minutes || ' minutes')::interval | ||
| ) | ||
| select | ||
| source, | ||
| direction, | ||
| count(*) filter (where success = false) as failures, | ||
| max(occurred_at) as last_failure_at, | ||
| 'delivery_failure_total' as alert_id, | ||
| 'error' as severity | ||
| from recent | ||
| where rn <= :consecutive | ||
| group by source, direction | ||
| having count(*) filter (where success = false) >= :consecutive; | ||
|
|
||
| -- ---------------------------------------------------------------------------- | ||
| -- 2) spike_5xx | ||
| -- >= :abs_5xx respostas 5xx OU >:rate_5xx das requests na janela. | ||
| -- ---------------------------------------------------------------------------- | ||
| with bucket as ( | ||
| select | ||
| source, | ||
| direction, | ||
| count(*) as total, | ||
| count(*) filter (where http_status between 500 and 599) as count_5xx | ||
| from public.webhook_delivery_metrics | ||
| where occurred_at > now() - (:window_minutes || ' minutes')::interval | ||
| group by source, direction | ||
| ) | ||
| select | ||
| source, | ||
| direction, | ||
| total, | ||
| count_5xx, | ||
| round(100.0 * count_5xx / nullif(total,0), 2) as pct_5xx, | ||
| 'spike_5xx' as alert_id, | ||
| 'error' as severity | ||
| from bucket | ||
| where total >= :min_samples | ||
| and ( | ||
| count_5xx >= :abs_5xx | ||
| or (count_5xx::float / nullif(total,0)) > :rate_5xx | ||
| ); | ||
|
|
||
| -- ---------------------------------------------------------------------------- | ||
| -- 3) spike_4xx | ||
| -- > :rate_4xx das requests com http_status 4xx. | ||
| -- ---------------------------------------------------------------------------- | ||
| with bucket as ( | ||
| select | ||
| source, | ||
| direction, | ||
| count(*) as total, | ||
| count(*) filter (where http_status between 400 and 499) as count_4xx | ||
| from public.webhook_delivery_metrics | ||
| where occurred_at > now() - (:window_minutes || ' minutes')::interval | ||
| group by source, direction | ||
| ) | ||
| select | ||
| source, | ||
| direction, | ||
| total, | ||
| count_4xx, | ||
| round(100.0 * count_4xx / nullif(total,0), 2) as pct_4xx, | ||
| 'spike_4xx' as alert_id, | ||
| 'warning' as severity | ||
| from bucket | ||
| where total >= :min_samples | ||
| and (count_4xx::float / nullif(total,0)) > :rate_4xx; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,71 @@ | ||||||||||||||||
| # Webhook Alerts → Sentry | ||||||||||||||||
|
|
||||||||||||||||
| Monitor read-only de `public.webhook_delivery_metrics` que dispara alertas no | ||||||||||||||||
| Sentry quando há falhas de delivery ou spikes de 4xx/5xx por `(source, | ||||||||||||||||
| direction)`. | ||||||||||||||||
|
|
||||||||||||||||
| ## Componentes | ||||||||||||||||
|
|
||||||||||||||||
| | Arquivo | Papel | | ||||||||||||||||
| |---|---| | ||||||||||||||||
| | `docs/observability/webhook-alerts-spec.sql` | SSOT das queries de detecção (parametrizadas, read-only). | | ||||||||||||||||
| | `supabase/functions/webhook-alerts-monitor/index.ts` | Cron edge function: detecta + envia eventos ao Sentry via envelope API. | | ||||||||||||||||
|
|
||||||||||||||||
| ## Thresholds (perfil conservador) | ||||||||||||||||
|
|
||||||||||||||||
| Janela: **5 minutos**. Mínimo de **10 amostras** para spikes (evita falso-positivo em baixo volume). | ||||||||||||||||
|
|
||||||||||||||||
| | Alert | Critério | Severity | | ||||||||||||||||
| |---|---|---| | ||||||||||||||||
| | `delivery_failure_total` | ≥3 falhas **consecutivas** por (source,direction) | `error` | | ||||||||||||||||
| | `spike_5xx` | ≥5 respostas 5xx **OU** >20% das requests | `error` | | ||||||||||||||||
| | `spike_4xx` | >40% das requests com 4xx | `warning` | | ||||||||||||||||
|
|
||||||||||||||||
| Para alterar thresholds, edite as constantes no topo de | ||||||||||||||||
| `webhook-alerts-monitor/index.ts` (SSOT do runtime) e os defaults no | ||||||||||||||||
| spec SQL. | ||||||||||||||||
|
Comment on lines
+24
to
+26
|
||||||||||||||||
| Para alterar thresholds, edite as constantes no topo de | |
| `webhook-alerts-monitor/index.ts` (SSOT do runtime) e os defaults no | |
| spec SQL. | |
| Para alterar thresholds do monitor, edite as constantes no topo de | |
| `webhook-alerts-monitor/index.ts` (SSOT do runtime). O spec SQL é apenas uma | |
| referência documental no estado atual e deve ser mantido em sincronia | |
| manualmente, sem afetar o comportamento do monitor. |
Copilot
AI
Apr 30, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O exemplo de cron usa Authorization: Bearer <ANON_KEY>, mas ANON_KEY é público e não autentica o caller. Como a edge function usa service_role internamente (bypass RLS) e pode disparar alertas, isso deveria ser protegido com um secret dedicado (header tipo X-Cron-Secret) ou outra forma de autenticação não-pública; atualize o exemplo para refletir esse gate.
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,125 @@ | ||||||||||||
| #!/usr/bin/env node | ||||||||||||
| /** | ||||||||||||
| * 📜 Client Structured-Logging Gate (rotas críticas) | ||||||||||||
| * ---------------------------------------------------------------- | ||||||||||||
| * Garante que toda invocação de edge function (`supabase.functions.invoke(...)`) | ||||||||||||
| * em arquivos das rotas críticas (auth, quote, mcp, magicUp, comparison, | ||||||||||||
| * connections) use `createClientLogger` E propague `headers: log.headers()` | ||||||||||||
| * para correlacionar request_id entre client e edge logs. | ||||||||||||
| * | ||||||||||||
| * Critério: arquivo monitorado contém `supabase.functions.invoke` → | ||||||||||||
| * DEVE conter `createClientLogger` E `log.headers()` (ou equivalente | ||||||||||||
| * via REQUEST_ID_HEADER) em pelo menos uma chamada. | ||||||||||||
| * | ||||||||||||
| * Allowlist: arquivos legados podem ser registrados em ALLOWLIST com data e | ||||||||||||
| * ticket — não pode crescer (snapshot 2026-04-27). | ||||||||||||
| * | ||||||||||||
| * Saída: | ||||||||||||
| * exit 0 → todos os arquivos monitorados conformes | ||||||||||||
| * exit 1 → arquivos faltando logger estruturado | ||||||||||||
| */ | ||||||||||||
| import { readFileSync, statSync } from 'node:fs'; | ||||||||||||
| import { resolve, dirname, relative } from 'node:path'; | ||||||||||||
| import { fileURLToPath } from 'node:url'; | ||||||||||||
| import { execSync } from 'node:child_process'; | ||||||||||||
|
Comment on lines
+22
to
+24
|
||||||||||||
| import { resolve, dirname, relative } from 'node:path'; | |
| import { fileURLToPath } from 'node:url'; | |
| import { execSync } from 'node:child_process'; | |
| import { resolve, dirname } from 'node:path'; | |
| import { fileURLToPath } from 'node:url'; |
Copilot
AI
Apr 30, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Este gate afirma que “toda invocação” de supabase.functions.invoke(...) deve propagar headers: log.headers(), mas o critério implementado só verifica se o arquivo contém createClientLogger( e log.headers() em QUALQUER lugar. Um arquivo pode ter múltiplas invocações e ainda passar mesmo com algumas chamadas sem headers. Sugestão: iterar por ocorrências de supabase.functions.invoke( e validar por chamada (heurística por bloco/linha) ou pelo menos exigir que TODAS as ocorrências contenham headers: próximo da invocação.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O baseline foi commitado com
generatedAt: nulle sem a estrutura gerada pelo próprio gate (pretty JSON + timestamp ISO). Como o script já temUPDATE_BASELINE=1para gerar o formato canônico, vale atualizar este arquivo usando o script para evitar confusão e garantir que o baseline refletirá exatamente o filtro aplicado (threshold/omit=dev).