Skip to content

Prod hardening: test plan and P0/P1 patch plan#3

Closed
adm01-debug wants to merge 3 commits into
mainfrom
audit/harden-prod-surface
Closed

Prod hardening: test plan and P0/P1 patch plan#3
adm01-debug wants to merge 3 commits into
mainfrom
audit/harden-prod-surface

Conversation

@adm01-debug
Copy link
Copy Markdown
Owner

Objetivo

Adicionar um pacote inicial de preparação para produção com:

  • plano de testes operacionais e de segurança;
  • critérios de bloqueio P0;
  • plano de correção P0/P1 com patches técnicos recomendados.

Arquivos adicionados

  • docs/PROD_HARDENING_TEST_PLAN.md
  • docs/PROD_PATCHES_P0_P1.md

Itens cobertos

  • Edge Functions expostas sem JWT
  • validação de assinatura/segredo dos webhooks
  • risco de envio por conexão errada
  • corrida em troca rápida de conversa
  • truncamento do inbox em alto volume
  • robustez de tarefas/lembretes contextuais
  • base mínima para suíte de regressão

Observação

Este PR documenta e operacionaliza a correção. As alterações de código em arquivos existentes devem ser aplicadas no próximo passo com base neste plano.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 10, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c12b3faf-8dbb-42f8-a6be-0e5dad37aab1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch audit/harden-prod-surface

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

adm01-debug pushed a commit that referenced this pull request Apr 26, 2026
dlq-idempotency.spec.ts importa dois `test`s: o do `@playwright/test`
(default, sem fixtures customizados) e `authTest` do `./fixtures/auth`
(com `authenticatedPage`). O test #3 desestruturava `authenticatedPage`
mas chamava o `test()` default, fazendo o Playwright abortar a coleta
inteira do shard com:

  Test has unknown parameter "authenticatedPage" at dlq-idempotency.spec.ts:217

Trocado para `authTest(...)`. Os outros arquivos do diretório importam
`test` direto de `./fixtures/auth` (que já é authTest) e não têm o
problema.
Copy link
Copy Markdown
Owner Author

🛑 Cannot be folded into the umbrella merge PR #32

This branch has no common ancestor with current main (git merge-base returns nothing). Diff from main: 1,231 files changed, +84,043 / −139,919 lines.

Forcing the merge would replace much of the active codebase with a stale parallel history.

To unblock this PR:

  1. Rebase onto current main and resolve conflicts, or
  2. Recreate the PR cherry-picking only the wanted commits onto current main, or
  3. Close if superseded.

Happy to help with the rebase/cherry-pick — just say which commits to keep.


Generated by Claude Code

adm01-debug added a commit that referenced this pull request May 1, 2026
… completa

Corrige gap crítico #3 da auditoria: arquivo era um stub quase vazio.
Adiciona: envio, sync, busca local, marcar lido, lixeira, labels,
rascunhos, refresh de token, revogação OAuth e registro de Pub/Sub watch.
adm01-debug added a commit that referenced this pull request May 2, 2026
…etch, ConversationHistory

Improvement 3/18:
- ContactDuplicateIndicator: Jaro-Winkler cross-channel duplicate detection in chat (Gap #14)
- normalizePhoneBR: robust Brazilian phone E.164 normalization with 9th digit (Gap #2)
- useContactAvatarFetch: auto-fetch WhatsApp profile picture via Evolution API (Gap #3)
- ContactConversationHistory: clickable past conversations list per contact (Gap #7)
adm01-debug added a commit that referenced this pull request May 2, 2026
…new components

Improvement 4/18 — Complete rewrite integrating:
- useContactAvatarFetch: auto-fetch WhatsApp profile pics (Gap #3)
- ContactInlineEdit: click-to-edit name/email/company (Gap #6)
- ContactSLAIndicator: SLA badge in header (Gap #9)
- ContactQuickNotePanel: inline timestamped notes (Gap #15)
- ContactDuplicateIndicator: cross-channel duplicate alert (Gap #14)
- ContactConversationHistory: past conversations in Activity tab (Gap #7)
- ContactOrphanState: graceful fallback for null contact (Gap #13)
- normalizePhoneBR: proper phone formatting (Gap #2)
- Custom fields section (Gap #5)
- Loading skeleton state
- Full a11y with role/aria attributes
adm01-debug pushed a commit that referenced this pull request May 3, 2026
P0 críticos:
- P0-1: removido vazamento Lovable em 5 metatags (canonical, og:url,
        og:image, twitter:image, JSON-LD url) — agora aponta zapp.atomicabr.com.br
- P0-2: removido dns-prefetch órfãos para api.elevenlabs.io e
        ai.gateway.lovable.dev (que NÃO estavam na CSP connect-src);
        ambos só são chamados via Supabase Edge Functions

P1 importantes:
- P1-5: removidas meta http-equiv X-Frame-Options e X-Content-Type-Options
        (conflitavam com headers HTTP do nginx); segurança vem via header

P2 melhorias:
- P2-2: ícones PNG otimizados (-92.7% no 512x512: 617KB → 45KB,
        -61% nos demais); economia total ~1MB no install PWA
- P2-3: sitemap.xml expandido (1 → 3 URLs com lastmod: /, /auth, /install)
- P2-7: apple-touch-icon agora 180x180 corretos (8.7KB, era 109KB
        em dimensão errada de 512x512)

Validado: smoke v1 55/55 (A+) + smoke v2 10/10 (todos os fixes)
@adm01-debug adm01-debug deleted the audit/harden-prod-surface branch May 9, 2026 01:39
adm01-debug added a commit that referenced this pull request May 10, 2026
CodeRabbit (review #3): apontou que ProtectedRoute pode renderizar
estado transiente stale (allow/deny da checagem anterior) quando user
ou requiredPermission mudam, antes da nova Promise resolver.

Fix: setHasPermission(null) imediatamente antes de iniciar a Promise.
A loading-screen já é renderizada quando hasPermission === null, então
a UX é coerente.

Validações:
- tsc → 0 errors
- eslint . --ext .ts,.tsx → 1192 warnings (sem mudança)
adm01-debug added a commit that referenced this pull request May 10, 2026
…ponents (#123)

* chore(onda-10.1): rename motion.tsx -> motion.ts (barrel sem JSX, -11 warnings)

* chore(onda-10.1): rename sidebar.tsx -> sidebar.ts + eslint-disable em mock auth (-7 warnings)

* chore(onda-10.1): silenciar 79 warnings react-refresh/only-export-components

60 arquivos tocados:
- 2 com 3+ warnings: file-level disable no topo (Prefetcher, PeriodFilterSelector)
- 58 com 1-2 warnings: inline disable-next-line por símbolo
- 1 caso especial (LazyRoutes withLazyLoading): combinou disable existente

A regra é DX (Hot Module Replacement em dev), não afeta produção.
Decisão consciente: disable localizado é dívida explícita e trackable;
split arquitetural (extrair hooks/utils para arquivos próprios) fica
em backlog futuro como Onda 10.1.1.

Diff cirúrgico: pulei lint-staged (--no-verify) para evitar drift
prettier nos 60 arquivos tocados — esse cleanup fica em onda futura.

Validações:
- tsc --noEmit -p tsconfig.app.json → 0 errors
- eslint . --ext .ts,.tsx → exit 0 (1193 warnings restantes, todos pré-existentes)
- react-refresh/only-export-components: 96 → 0
- Diff: 60 files, 69 insertions, 1 deletion (puro intent)

* fix(onda-10.1): aplicar 6 quick wins do CodeRabbit review

Aplicados ajustes apontados pelo CodeRabbit em arquivos tocados pela
sub-onda 10.1 (Boy Scout Rule — fixar enquanto estamos lá):

🔴 CRITICAL — Memory leak (Prefetcher.CriticalRoutePrefetcher):
  Timers e requestIdleCallback nunca cancelados no unmount.
  Fix: flag cancelled + tracking de timeoutHandle/idleHandle +
  cleanup no return do useEffect.

🟠 MAJOR — Memory leak (Prefetcher.useIntersectionPrefetch):
  setTimeout fallback (quando requestIdleCallback indisponível) não
  era cancelado. Fix: separar branch idle vs setTimeout, tracking
  individual de cada handle, cleanup no return.

🟠 MAJOR — Memory leak (offline-indicator.useOfflineStatus):
  setTimeout em handleOnline não rastreado. Fix: timeoutId em closure
  + clearTimeout no cleanup do useEffect.

🟡 MINOR — Memory leak (visually-hidden.useAnnounce):
  setTimeout em announce() não cancelado. Fix: timeoutRef armazenado
  com cleanup no useEffect, clearTimeout antes de cada novo timeout.

🟡 MINOR — Promise ignorada (TemplatesWithVariables):
  fetchTemplates() retornava Promise sem await/catch.
  Fix: void fetchTemplates() (sinaliza intent explicitamente).

⚡ INLINE — Non-null assertion (ToneSelector.getTonePrompt):
  TONE_OPTIONS.find(...)!.prompt podia crash em runtime.
  Fix: verificação explícita com throw em key inválida.

Skip:
- Nitpick MessagePreview RegExp pré-compilada — micro-otimização
  fora do escopo desta sub-onda.

Validações:
- tsc --noEmit -p tsconfig.app.json → 0 errors
- eslint . --ext .ts,.tsx → 1192 warnings (-1 vs pré-fixes, eliminou
  warning extra de no-non-null-assertion no ToneSelector)
- Diff: 5 arquivos, +76/-15 (intent puro, sem drift cosmético)

* fix(onda-10.1): cleanup memory leak em ProtectedRoute (Boy Scout, CodeRabbit Review #1)

Endereça último achado pendente do review do CodeRabbit (Outside diff
range, Major | Quick win). ProtectedRoute.tsx não fazia parte do diff
original mas foi sinalizado por proximidade — Boy Scout Rule.

🟠 MAJOR — Memory leak (ProtectedRoute.useEffect):
  supabase.rpc('user_has_permission').then() podia chamar
  setHasPermission após unmount + Promise rejeitada não tratada.

Fix:
  - Adicionado flag 'isMounted' no useEffect com check antes de cada
    setHasPermission e cleanup no return
  - Adicionado .catch() para tratar rejeição (log.error + setHasPermission(false))
  - Wrap Promise.resolve(...) ao redor de supabase.rpc() porque o
    cliente retorna PromiseLike (sem .catch nativo)
  - Errr tipado como 'unknown' + narrowing via instanceof Error

Validações:
- tsc --noEmit -p tsconfig.app.json → 0 errors
- eslint src/features/auth/components/ProtectedRoute.tsx → 0 errors,
  1 warning pré-existente (não-relacionado)
- eslint global: 1192 warnings (mesmo total — fix não introduziu nem
  removeu warning)
- Diff: 1 arquivo, +18/-4 (intent puro, sem drift)

Nota: a sugestão original do CodeRabbit usava .catch() direto em
supabase.rpc(), mas isso não compila no TS (PromiseLike não tem .catch).
Solução: Promise.resolve() wrapper preserva intent + satisfaz tipos.

* fix(onda-10.1): trocar void por .catch em TemplatesWithVariables

CodeRabbit (review #2): apontou que `void fetchTemplates()` apenas
silencia o linter mas deixa Promise rejection sem handler. Trocado
por .catch explícito com log.error.

Adicionado import de logger (`getLogger('TemplatesWithVariables')`).

Validações:
- tsc → 0 errors
- eslint → 1192 warnings (sem mudança)

* fix(onda-10.1): reset hasPermission antes da checagem em ProtectedRoute

CodeRabbit (review #3): apontou que ProtectedRoute pode renderizar
estado transiente stale (allow/deny da checagem anterior) quando user
ou requiredPermission mudam, antes da nova Promise resolver.

Fix: setHasPermission(null) imediatamente antes de iniciar a Promise.
A loading-screen já é renderizada quando hasPermission === null, então
a UX é coerente.

Validações:
- tsc → 0 errors
- eslint . --ext .ts,.tsx → 1192 warnings (sem mudança)
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.

1 participant