En todo el producto no había ningún evento que dijera “ha llegado un informe nuevo”. Los avisos llegaban a un buzón y a ningún otro sitio, así que un equipo que quisiera sus informes en n8n, en Zapier o en un script propio no tenía adónde apuntarlos.

Ahora hay un destino: una URL tuya, los eventos que quieras y una clave de firma. Cuatro eventos, nombrados una vez y escritos igual en todas partes - report.created, report.status_changed, report.severity_changed y report.first_viewed. Son una interfaz desde el momento en que salen, porque alguien mete uno en un flujo y un cambio de nombre lo rompe en silencio, así que el contenido lleva su propia versión desde la primera publicación.

Lo que conviene saber antes de apuntar algo hacia ahí:

  • Todo POST va firmado. HMAC-SHA256 sobre la marca de tiempo y el cuerpo en bruto, con la marca de tiempo dentro del material firmado, de modo que una petición capturada no se te pueda repetir más tarde.
  • El destino pertenece al equipo, no a una persona. La dirección tiene que seguir funcionando cuando se marche quien la añadió.
  • Los fallos se reintentan y luego paran. La espera progresiva dura unas ocho horas y media antes de rendirse.
  • Cada intento queda registrado. “¿Se disparó el webhook?” se responde desde /app/webhooks y no desde los registros de producción de alguien.

Una URL que aporta el cliente es, por construcción, una superficie de falsificación de peticiones, así que la dirección se vuelve a resolver y a comprobar justo antes de cada conexión, y no solo cuando se guardó, nunca se sigue una redirección, y el cuerpo de la respuesta se lee solo lo justo para cerrar el flujo.

La página que abre un desconocido

La página compartida de un informe es tanto la demostración del producto como la herramienta de triaje de su dueño, y las dos cosas quieren algo distinto de la misma pantalla.

El orden de las pestañas se decidió pensando en el dueño: lo que escribió quien informó va primero, porque es lo más valioso de un informe y lo más corto. Eso es acertado para quien trabaja su propio buzón y equivocado para aquel a quien se le envió el enlace, que llegaba y se encontraba con siete palabras con las que no puede hacer nada, mientras los errores de consola, las peticiones fallidas y la grabación quedaban a un clic y sin abrir. Quien no es el dueño del informe ahora aterriza en las pruebas, y la franja que hay sobre las pestañas dice qué contiene la captura, en lugar de dejarlo para que se descubra.

El reproductor tenía dos defectos, ambos visibles en una sola captura de pantalla. Su superficie oscura se extendía por todo el ancho de la página, así que cualquier grabación más estrecha que esa banda - en una pantalla ancha, casi todas - quedaba varada en medio de un campo negro. Y se abría en pausa sobre el fotograma cero, que es lo que hubiera en la pantalla de quien informó mientras todavía alargaba la mano hacia el botón de grabar. La superficie oscura ahora es relleno del propio elemento de vídeo, así que se ciñe a la grabación con cualquier proporción, y el reproductor se abre en un fotograma elegido por merecer la pena mirarlo.

A quien informa se le dice qué pasa con su grabación

El registro de interacciones se niega a guardar lo que alguien haya escrito en un campo de contraseña. El registro de red oculta los valores de credenciales por defecto. El vídeo muestra la pantalla en la que se escribieron, y sobre eso no actúa nada.

La misma pregunta, dos respuestas distintas - y quien mejor puede zanjarla es quien informa, porque es el único que sabe si importa la ficha de cliente que está abierta detrás del error. Así que ahora se le dice en qué se convierte la grabación antes de empezarla, y no después.

Ese mismo instinto está detrás de tres cambios más pequeños en el otro lado. Que alguien lea su propio informe nunca ha contado para su número de visitas, pero eso no se decía en ninguna parte; la página del informe, la lista de informes y el resumen ahora explican cada uno por qué el número no los incluye.

Session Replay

Extensión gratuita de Chrome. Un clic en la página que se está portando mal captura la captura de pantalla, la consola y el registro de red, y te devuelve un enlace para pegar en el ticket.

Instalar la extensión

La captura salió más barata, y su ocultación quedó coherente

Una captura corre a la densidad de píxeles de la pantalla salvo que se le diga otra cosa, así que una pantalla 6K grababa 6016 píxeles de ancho. Esos píxeles se pagaban tres veces: en la subida de quien informa, donde se comen el presupuesto de bytes del plan y por tanto la duración que se le permite grabar; en el almacenamiento, porque el original se conserva sea cual sea lo que se codifique junto a él; y en cada transcodificación, donde descodificar es la mitad cara. Medido en producción, un clip de treinta y dos segundos costaba nueve minutos y cincuenta y cuatro segundos de CPU. La captura ahora está limitada a 1920 de ancho.

El arreglo de la ocultación es el más importante. Un dominio con derecho a capturar sin ocultación rebajaba la máscara de cabeceras y cadena de consulta hasta dejar solo los nombres de tarjeta, de modo que una misma grabación podía ocultar un campo de formulario llamado api_key y a la vez conservar en claro una cabecera X-Api-Key, un token CSRF y un ?password= de la misma petición. Nadie eligió eso: eran dos caminos por un mismo archivo que respondían distinto a la misma pregunta. Las credenciales en cabeceras y en URLs se enmascaran también en un dominio con derecho, y un campo de pasaporte que la lista de credenciales se estaba tragando ahora se detecta bien.

Tres cosas que dijimos que haríamos

Vale la pena enumerarlas juntas, porque de cada una se escribió aquí antes de hacerla.

La extensión ya no expone sus iconos y sus sonidos a todos los orígenes. web_accessible_resources concedía los dos directorios a todas las URLs y ninguna página cargó jamás nada de ahí - una extensión lee sus propios archivos empaquetados sin ningún permiso. Era la hermana del permiso sin usar del que escribimos el domingo, y las dos ya no están; la extensión 1.6.2 está en la tienda.

La biblioteca de integración se sirve desde nuestras propias páginas como 0.4.0, así que identify() llevando cualquier clave que pudiera llevar una etiqueta meta ya no es cierto solo para quien instala desde npm. Se han corregido tres sitios que todavía decían a los dueños de las webs que acepta cinco claves.

Y a las doce y media de esta madrugada el servidor MCP empezó a decir en el saludo inicial que el texto que retransmite es de terceros: que el comentario, el título, la URL y el nombre de un informe los escribió quien presentó el error, normalmente una persona del público, y que se deben tratar como datos que leer y citar, nunca como instrucciones que seguir. Lo registramos ayer, sobre la base de la investigación de GhostSplice, y escribimos por qué.

Cosas más pequeñas

  • El dueño de la suscripción es quien manda a la hora de cancelarla y reanudarla.
  • Un dominio reclamado se llama dominio en todos los sitios donde la interfaz lo nombra, en lugar de sitio en la mitad de ellos.
  • Las imágenes de los artículos de aquí pueden llevar pie, texto alternativo y un tamaño, y se ajustan al ancho que les da el artículo.
  • Las páginas de condiciones y de seguridad están traducidas a los otros seis idiomas, y toda página estática declara ahora el idioma en el que está escrita de verdad.
  • La API entrega una sesión por token en lugar de una por renovación, y la página de tokens se lee como una fila por token.