Pular para o conteúdo

Redação de PII

A redação acontece na borda do runner, depois que o connector responde e antes de o resultado cruzar a fronteira. Um único ponto de aplicação: não espalhado, não repetido do outro lado.

Ela está ativa quando RUNNER_CONNECTORS=real. No caminho sintético os dados não carregam PII, então só a camada de campo declarado roda.

Mascara os caminhos que o manifest do connector declara em sensitiveFields. É dot-path puro, por design.

Uma passada indexada por operação, para as formas que uma regex não enxerga: valores de property bags arbitrários e identificadores crus de usuário final.

Ela cobre exatamente cinco operações do Amplitude:

Operação Mascara Redige
analytics.searchUser userId properties
analytics.getUserActivity nenhum properties, deviceId
replays.listSessionReplays userId nenhum
replays.getSessionReplay userId nenhum
replays.getSessionReplayEvents nenhum properties

Nas property bags, as chaves são preservadas e os valores são redigidos, e o formato do dado continua legível para diagnóstico, o conteúdo não.

Para qualquer outra operação, esta camada é no-op.

Passada final, sensível a conteúdo, por regex sobre qualquer string que tenha sobrado: e-mail, CPF, IPv4, cartão de crédito com validação de Luhn e telefone, mais um conjunto de nomes de campo sensíveis.

Não é orientada a dot-path, então ela complementa as duas anteriores e funciona como defesa em profundidade.

O resultado de uma operação não é o único dado que atravessa a fronteira. Estes são todos os canais, e o que cada um atravessa:

Canal Redação
Resultado e erro de operação As três camadas.
detail do probe de credencial, que vai à Frota As mesmas três camadas, pelo mesmo redator, antes de truncar a mensagem.
Avisos do coletor numa captura que deu certo, e o motivo de uma captura que falhou (o stderr do coletor e do agregador), que vão à tela de Profiling As três camadas, pelo mesmo redator. Os avisos são cortados em 400 caracteres e 20 entradas depois da redação.
Learned-state (topologia, registros DNS, rotas de load balancer) O motor de conteúdo, ao sair do Agent, com uma exceção para IP, descrita abaixo.
Assinatura do Profiler Nenhuma. Ela carrega símbolo de código e peers de rede, e o que sai está listado em Profiler.

IP de infraestrutura não é PII; IP em texto livre é. No resultado, a camada de conteúdo mascara todo IPv4, porque num texto qualquer ela não distingue o endereço de um cliente do de um servidor. O learned-state distingue: um registro DNS é configuração da sua infraestrutura, e o IP dentro dele (alvo de um registro A, frontend de um load balancer, ip4: de um SPF) é endereço de recurso seu, e é o que permite responder “este endereço cai em qual serviço?”. Por isso o IP passa só dentro do nome e do alvo de um registro DNS, e em qualquer outro campo do learned-state é mascarado como no resultado. É a mesma linha que a camada de conteúdo já traça para blocos CIDR. E-mail, CPF, cartão e telefone são redigidos em qualquer campo, inclusive dentro de um registro DNS: o rua=mailto: de um registro DMARC sai como [EMAIL].

Esta é a pegadinha que mais gera garantia falsa, então vale ser direto.

O FieldRedactor é dot-path puro. Declarar no manifest um campo que só existe dentro de um array (por exemplo um campo de um RumErrorSample[]) não redige nada. E isso é pior do que não declarar, porque produz a aparência de uma garantia que não existe.

Para esse caso, a regra vai no redactStructured, que é indexado por operação e atravessa arrays.

Janela do terminal
WHYSTACK_PII_REDACTION=off

Existe um alias legado, INFRAINTEL_PII_REDACTION. Quando ambos estão presentes, o nome novo vence.

Seja preciso sobre o alcance dele:

Camada WHYSTACK_PII_REDACTION=off
1. Campo declarado continua rodando
2. Estrutural desligada
3. Conteúdo desligada

O kill-switch vale para todos os canais da tabela acima que passam pela camada de conteúdo, inclusive o learned-state.

Com as camadas desligadas não há redação a auditar, então o rastro não é linha de audit. São três outros:

  • Em produção, o Agent recusa subir. Com off, RUNNER_CONNECTORS=real e NODE_ENV=production (o valor da imagem), o boot falha nomeando a variável.
  • Fora disso, ele sobe com um aviso no log de boot.
  • E anuncia o estado ao control-plane quando conecta, e a Frota mostra o Agent como sem redação.

Classificação divergente: a borda expõe, não reescreve

Seção intitulada “Classificação divergente: a borda expõe, não reescreve”

Um caso que aparece na prática e que vale entender antes de reportar como bug.

O Datadog manda todo stderr para status:error. Então uma busca por erro pode devolver [INFO] Worker exiting. A fonte classificou como erro, o texto diz outra coisa.

A borda expõe a divergência em vez de reescrever o campo: além do level da fonte, ela emite textLevel e levelMismatch. O princípio é que a classificação da fonte é um fato sobre a fonte, e corrigi-la em silêncio apagaria informação.

O quê Onde
Composição das três camadas apps/runner/src/redact/pii-redactor.ts
Camada de campo declarado apps/runner/src/redact/field-redactor.ts
Camada estrutural, por operação packages/core-edge/src/policy/structured-redactor.ts
Motor de conteúdo packages/core-edge/src/policy/pii-redactor.ts
Kill-switch packages/core-edge/src/policy/kill-switch.ts, e a recusa no boot em apps/runner/src/boot.ts
Learned-state ao sair do Agent apps/runner/src/learned/redact.ts
detail do probe de credencial apps/runner/src/connectors/health.ts
Avisos do coletor e motivo de captura que falhou apps/runner/src/profiling/push.ts