Redacción de PII
La redacción ocurre en el borde del runner, después de que el connector responde y antes de que el resultado cruce la frontera. Un único punto de aplicación: ni disperso ni repetido del otro lado.
Está activa cuando RUNNER_CONNECTORS=real. En el camino sintético los datos no llevan PII, así que
solo corre la capa de campo declarado.
Las tres capas, en este orden
Sección titulada «Las tres capas, en este orden»1. Campo declarado: FieldRedactor
Sección titulada «1. Campo declarado: FieldRedactor»Enmascara las rutas que el manifest del connector declara en sensitiveFields. Es dot-path puro,
por diseño.
2. Estructural: redactStructured
Sección titulada «2. Estructural: redactStructured»Una pasada indexada por operación, para las formas que una regex no ve: valores arbitrarios de property bags e identificadores crudos de usuario final.
Cubre exactamente cinco operaciones de Amplitude:
| Operación | Enmascara | Redacta |
|---|---|---|
analytics.searchUser |
userId |
properties |
analytics.getUserActivity |
ninguno | properties, deviceId |
replays.listSessionReplays |
userId |
ninguno |
replays.getSessionReplay |
userId |
ninguno |
replays.getSessionReplayEvents |
ninguno | properties |
En las property bags, las claves se conservan y los valores se redactan, y la forma de los datos sigue legible para el diagnóstico, el contenido no.
Para cualquier otra operación, esta capa es un no-op.
3. Contenido: redactPii
Sección titulada «3. Contenido: redactPii»Una pasada final por regex, sensible al contenido, sobre las cadenas que hayan quedado: email, CPF, IPv4, tarjeta de crédito con validación de Luhn y números de teléfono, más un conjunto de nombres de campo sensibles.
No se guía por dot-path, así que complementa las dos capas anteriores y funciona como defensa en profundidad.
Qué más sale del Agent, y cómo se redacta
Sección titulada «Qué más sale del Agent, y cómo se redacta»El resultado de una operación no es el único dato que cruza la frontera. Estos son todos los canales, y por lo que pasa cada uno:
| Canal | Redacción |
|---|---|
| Resultado y error de operación | Las tres capas. |
detail del probe de credenciales, que llega a la Flota |
Las mismas tres capas, por el mismo redactor, antes de truncar el mensaje. |
| Advertencias del recolector de una captura que salió bien, y el motivo de una captura que falló (el stderr del recolector y del agregador), que llegan a la pantalla de Profiling | Las tres capas, por el mismo redactor. Las advertencias se cortan a 400 caracteres y 20 entradas después de la redacción. |
| Learned-state (topología, registros DNS, rutas de load balancer) | El motor de contenido, al salir del Agent, con una excepción para IPs, descrita abajo. |
| Firma del Profiler | Ninguna. Lleva símbolos de código y peers de red, y lo que sale está listado en Profiler. |
Una IP de infraestructura no es PII; una IP en texto libre sí. En un resultado, la capa de
contenido enmascara toda IPv4, porque en un texto arbitrario no puede distinguir la dirección de un
cliente de la de un servidor. El learned-state sí puede: un registro DNS es configuración de tu
infraestructura, y la IP dentro de él (el destino de un registro A, el frontend de un load balancer, un
ip4: de SPF) es la dirección de un recurso tuyo, y es lo que responde “¿en qué servicio cae esta
dirección?”. Por eso una IP pasa solo dentro del nombre y del destino de un registro DNS, y en
cualquier otro campo del learned-state se enmascara como en un resultado. Es la misma línea que la capa
de contenido ya traza para los bloques CIDR. Emails, CPFs, tarjetas y números de teléfono se redactan en
cualquier campo, incluso dentro de un registro DNS: el rua=mailto: de un registro DMARC sale como
[EMAIL].
La trampa: sensitiveFields no cruza arrays
Sección titulada «La trampa: sensitiveFields no cruza arrays»Esta es la trampa que más a menudo produce una garantía falsa, así que vamos al grano.
FieldRedactor es dot-path puro. Declarar en el manifest un campo que solo existe dentro de un
array (por ejemplo, un campo de un RumErrorSample[]) no redacta nada. Y eso es peor que no
declararlo, porque produce la apariencia de una garantía que no existe.
Para ese caso, la regla va en redactStructured, que está indexado por operación y sí cruza
arrays.
El kill switch, y lo que no desactiva
Sección titulada «El kill switch, y lo que no desactiva»WHYSTACK_PII_REDACTION=offHay un alias legado, INFRAINTEL_PII_REDACTION. Cuando ambos están presentes, gana el nombre
nuevo.
Sé preciso sobre su alcance:
| Capa | Con WHYSTACK_PII_REDACTION=off |
|---|---|
| 1. Campo declarado | sigue corriendo |
| 2. Estructural | desactivada |
| 3. Contenido | desactivada |
El kill switch vale para todos los canales de la tabla de arriba que pasan por la capa de contenido, incluido el learned-state.
Con las capas desactivadas no hay redacción que auditar, así que el rastro no es una línea de auditoría. Hay otros tres:
- En producción, el Agent se niega a arrancar. Con
off,RUNNER_CONNECTORS=realyNODE_ENV=production(el valor de la imagen), el boot falla nombrando la variable. - Fuera de eso, arranca con una advertencia en el log de boot.
- Y anuncia el estado al control-plane cuando se conecta, y la Flota muestra el Agent como corriendo sin redacción.
Clasificación divergente: el borde expone, no reescribe
Sección titulada «Clasificación divergente: el borde expone, no reescribe»Un caso que aparece en la práctica y que conviene entender antes de reportarlo como bug.
Datadog manda todo stderr a status:error. Así que una búsqueda de errores puede devolver
[INFO] Worker exiting. La fuente lo clasificó como error, el texto dice otra cosa.
El borde expone la divergencia en lugar de reescribir el campo: junto al level de la fuente,
emite textLevel y levelMismatch. El principio es que la clasificación de la fuente es un hecho
sobre la fuente, y corregirla en silencio borraría información.
Dónde auditar
Sección titulada «Dónde auditar»| Qué | Dónde |
|---|---|
| La composición de las tres capas | apps/runner/src/redact/pii-redactor.ts |
| Capa de campo declarado | apps/runner/src/redact/field-redactor.ts |
| Capa estructural, por operación | packages/core-edge/src/policy/structured-redactor.ts |
| Motor de contenido | packages/core-edge/src/policy/pii-redactor.ts |
| Kill switch | packages/core-edge/src/policy/kill-switch.ts, y el rechazo en el boot en apps/runner/src/boot.ts |
| Learned-state al salir del Agent | apps/runner/src/learned/redact.ts |
detail del probe de credenciales |
apps/runner/src/connectors/health.ts |
| Advertencias del recolector y motivo de una captura fallida | apps/runner/src/profiling/push.ts |