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.
As três camadas, nesta ordem
Seção intitulada “As três camadas, nesta ordem”1. Campo declarado: FieldRedactor
Seção intitulada “1. Campo declarado: FieldRedactor”Mascara os caminhos que o manifest do connector declara em sensitiveFields. É dot-path puro,
por design.
2. Estrutural: redactStructured
Seção intitulada “2. Estrutural: redactStructured”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.
3. Conteúdo: redactPii
Seção intitulada “3. Conteúdo: redactPii”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 que mais sai do Agent, e com que redação
Seção intitulada “O que mais sai do Agent, e com que redação”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].
A armadilha: sensitiveFields não atravessa array
Seção intitulada “A armadilha: sensitiveFields não atravessa array”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.
O kill-switch, e o que ele não desliga
Seção intitulada “O kill-switch, e o que ele não desliga”WHYSTACK_PII_REDACTION=offExiste 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=realeNODE_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.
Onde auditar
Seção intitulada “Onde auditar”| 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 |