O Sentry 11 trocou `sendDefaultPii` por `dataCollection`, com default AMPLO
(IP, cookies, corpo de requisição/resposta, entrada/saída de IA, dados de
query, filas). Omitir a opção, que era o conserto óbvio para o TS2353, faria o
DSN da comunidade receber dado de titular de terceiros (issue #100).
- lib/sentry/privacidade.ts: ponto único com `dataCollection` restritivo
explícito (base "v10" do MIGRATION.md + stackFrameVariables: false) e os
hooks de scrub; espalhado nos 4 Sentry.init (server, edge, client, worker).
- lib/sentry/scrub.ts: beforeSendSpan no formato streamed (name/attributes),
limpando também `sentry.segment.name`, headers-credencial/IP, corpo, user.* e
client.address; beforeSend apaga request.data, request.cookies e user e passa
a mensagem por scrubUrl (o token do path saía na mensagem do erro);
beforeSendTransaction removido (no-op no 11, confirmado pelo guia).
- lib/sentry/privacidade.sdk.test.ts: prova pelo envelope do SDK real, com
transporte de captura e controles positivos; 4 sabotagens ficam vermelhas.
- withSentryConfig de @sentry/nextjs/config; enableLogs removido; AGENTS.md
cita Sentry 11; fragmento em .changes/.
Substitui #1743.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A borda de letra e hífen posta nos padrões de CPF e de telefone colado
parou de comer pedaço de UUID, mas passou a deixar sair inteiro o número
grudado nos rótulos que alguém digita colado: "cpf12345678909",
"fone11987654321", "tel-11987654321", "lead-123.456.789-09". É o mesmo
texto que vai à TypeSafe, e a tela de aceite promete apagar CPF e
telefone antes de sair.
Agora o UUID é separado do texto pela forma exata (8-4-4-4-12 em
hexadecimal) antes dos padrões numéricos, que voltam a não ter borda de
letra. Medido em 80.000 textos com UUID: 0 alterados (a main alterava
1.988). A borda do telefone de 8 dígitos fica, com o motivo corrigido:
sem ela, 1.390 de 5.000 SHA-1 saíam alterados.
Sabotado nos dois sentidos: sem a separação do UUID, o teste dos 5.000
UUIDs reprova; com as bordas de antes, os dois testes do número grudado
reprovam.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015PWGNXXzCQURHMALcEMmkC
Aceitar hífen entre os blocos do CPF sem borda fora deles fazia o
padrão casar dentro de UUID: em 5.000 UUIDs gerados, 67 casavam no
CPF, e o padrão do telefone colado em texto (anterior a esta frente)
casava em outros 126 — 141 saíam alterados no total. O CPF ganha borda
sem letra, dígito ou hífen vizinho; o telefone colado ganha borda só de
hexadecimal e hífen, os vizinhos possíveis dentro de um UUID, para
seguir pegando o número grudado em palavra ("zap11987654321").
O teste do UUID fixo passava por sorte; agora são 5.000 UUIDs
determinísticos (sha256 do índice), e sem qualquer uma das duas bordas
ele reprova.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015PWGNXXzCQURHMALcEMmkC
O aceite de LGPD do Jev promete que CPF e telefone saem apagados antes de a
mensagem ir à TypeSafe. "11-98765-4321", "11.98765.4321", "123 456 789 09",
"123.456.789.09" e "123-456-789-09" passavam inteiros. O DDD agora aceita
hífen ou ponto, o 9 da frente pode vir solto, e o CPF aceita qualquer
separador entre os blocos. Os casos de UUID, data e hora seguem intactos.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015PWGNXXzCQURHMALcEMmkC
O padrão exigia o DDD colado ao número e sem parênteses. "(11) 98765-4321",
"98765-4321" e "3456-7890" passavam inteiros — para o Sentry e para o Jev,
cuja tela promete ao admin, na hora do aceite, que o telefone sai apagado.
- Padrão novo com borda: +55 e DDD opcionais, parênteses opcionais, 8 ou 9
dígitos com hífen, espaço ou nada. A borda impede comer pedaço de UUID.
- O padrão antigo fica depois do CPF, para o número colado em outro texto.
- E-mail passa a ser apagado antes dos números: o telefone comia dígitos de
dentro do endereço e deixava o resto passar.
Medido no código antigo: 2 casos novos vermelhos; no novo, verdes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015PWGNXXzCQURHMALcEMmkC
Onda 1 da integração do Jev (TypeSafe AI). Nada de worker, tela ou rota
nova ainda; o que entra é o que as próximas ondas consomem.
- Provedores: lista IRMÃ `PROVEDORES_DE_DECISAO` (typesafe) e a união
`PROVEDORES_COM_CHAVE`/`IDS_COM_CHAVE`. `PROVEDORES` não muda, então
nenhuma superfície de conversa enxerga o Jev. Só as de CHAVE pedem a
união (rota de credenciais, diálogo, cartão, lista, rotação, hook,
validador). A lista de credenciais agrupava só por `PROVEDORES` e
descartava a chave do Jev; o aviso "sem chave" do painel e o da lista
contam só chave de quem conversa.
- Catraca testada: o Jev fora do registry, do ensaio, do schema de
versão, do PUT/PATCH do painel e do passo da chave do onboarding; e
quem usa a união tem de estar declarado.
- Validador: `GET {base}/v1/models`, que exige a chave e não gasta
token (401/403 → auth_failed_401; lê `models[].name`).
- Cliente: versão fixada `jev-1.13.0`, teto 1,5 s, eixo `exigeAcao`
(401/403/402/4xx não mapeado/400/422), `sem_credito`, `retryAfterMs`,
base em `JEV_API_BASE_URL` (vazio = api.typesafe.ai). O 401 deixa de
ser "defeito nosso".
- Disjuntor em memória por organização, com relógio injetável.
- Interruptor em `organizations.settings.jev` (Zod, sem migration):
leitura nunca lança e falha desligada; ligado sem aceite = desligado.
- `chaveDaOrganizacao` real: credencial typesafe ativa e validada mais
recente da organização, decifrada; nunca lança.
- Preço na tabela única (`pricing.ts`): 4,2 ¢/Mtok de entrada, saída
grátis, id exato — custo fracionário, fora do Math.ceil.
- `log-invocation`: `typesafe/…` deixa de virar OpenRouter; aceita
provider e origem_da_escolha explícitos.
- Redação da chave `apikey_…` nos dois redatores.
- Registro: `decisaoRapida` em sentiment_classify, com teste de
chamador nos dois sentidos.
- Espanhol dos textos novos, e o gate que faltava para `quandoUsar` e
textos do registro (pegou a DeepSeek e mais cinco textos antigos).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015PWGNXXzCQURHMALcEMmkC
O lint-channels reprovou lib/sentry/scrub.ts: a doutrina de restrição de canal
(invariante 1) proíbe nomear provider fora de lib/channels/, e o arquivo citava
um nos comentários e na lista de headers. Os três configs do Sentry ficam na
raiz, fora de ROOTS, então nunca foram varridos — mover a lógica para lib/ a
colocou sob o guard. O guard está certo.
As duas correções deixam o código melhor, não só conforme:
- O segmento do canal no CREDENTIAL_PATH virou [^/]+ em vez de uma lista de
provider. Rota de webhook de canal NOVO ganha a proteção sozinha, em vez de
vazar token até alguém lembrar de somar o nome à regex.
- Header sensível passa a ser casado por PADRÃO (authorization|cookie|api-key|
token|secret|password|credential) em vez de lista fechada. Mesma lógica:
header de integração nova nasce coberto. Tem teste com um header inventado.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4NxbuaBH2rNizak9HkDpd
O beforeSend cobria erro e mensagem, e vivia TRIPLICADO verbatim nos três
runtimes. Transação, span e breadcrumb têm hooks próprios — que não existiam
em lugar nenhum. Com tracesSampleRate: 1, o canal sem sanitização era
justamente o de 100% de amostragem.
Medido com um receptor no lugar do Sentry, usando a config exata do repo: a
URL crua saía em transaction, request.url, url.full, http.url, url.path e
http.target. Quatro rotas têm credencial no path, e o token do webhook do WAHA
é a credencial ÚNICA daquela rota na instalação padrão —
WAHA_WEBHOOK_REQUIRE_SIGNATURE nasce false porque o WAHA Core não assina, e o
Caddyfile documenta que a rota por tenant continua pública apoiada em o token
ser imprevisível. A telemetria publicava esse token.
- lib/sentry/scrub.ts: ponto único com os quatro hooks. Redige o token das 4
rotas em que ele é credencial e o VALOR da query preservando a chave. Os 134
segmentos [id] são UUID e ficam de fora de propósito: redigir tudo cegamente
tornaria o Sentry inútil pra depurar, que é o oposto do objetivo.
- No DSN da comunidade agora vai só ERRO: tracesSampleRate 0 e
replaysSessionSampleRate 0. Quem aponta pro próprio Sentry recebe tudo,
porque aí o dado não sai da infraestrutura de quem é dono dele.
- install.sh pergunta durante a instalação; em --yes sem SENTRY_DSN, desligado.
- Removidas as rotas de exemplo do assistente do Sentry: sentry-example-api era
um GET sem auth que lançava de propósito, ou seja, qualquer um podia gerar
evento no Sentry dos mantenedores à vontade.
Um defeito do próprio scrub só apareceu ao rodar o envelope inteiro: o Sentry
preenche request.query_string com a query CRUA, sem '?' na frente, e a primeira
versão do regex exigia o prefixo — a assinatura sobrevivia nesse campo. Tem
teste de regressão.
Relatado por @integralmarketingmx na issue #100, com diagnóstico próprio e uma
autocorreção sobre os Session Replays.
Refs #100
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C4NxbuaBH2rNizak9HkDpd