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.
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”ROOTPILOT_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 | ROOTPILOT_PII_REDACTION=off |
|---|---|
| 1. Campo declarado | continua rodando |
| 2. Estrutural | desligada |
| 3. Conteúdo | desligada |
As camadas desligadas auditam a decisão em stderr, então o desligamento não é silencioso.
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 |