- La razón por la que el sitio de David Bushell se veía roto desde hace tiempo para algunos usuarios fue un CSS que la extensión de navegador de Grammarly inyectó en la página de forma encubierta
- En Firefox, la extensión de Grammarly inserta una hoja de estilos desde recursos locales de la extensión, es difícil de encontrar desde el
StyleSheetList de la página web y además evade la Content Security Policy
- El conflicto ocurrió porque Grammarly definió globalmente
--rem:16 en :root, y el sitio usaba el mismo nombre --rem para calcular su tipografía fluida
- El
--rem del sitio estaba dentro de una cascade layer, y debido a la regla de CSS por la que los estilos fuera de capas tienen prioridad, el valor de Grammarly podía sobrescribir el cálculo
- Como medida temporal resistieron con un mutation observer y
!important, pero la respuesta final fue cambiar el nombre de la propiedad a --🤡; si una extensión inyecta nombres comunes en el :root global, puede chocar fácilmente con cualquier página web
El CSS de Grammarly que entró en la página
- Durante varios meses hubo reportes esporádicos de que el layout del sitio se desalineaba y los tamaños se veían raros, acompañados por capturas de pantalla
- Lectores con conocimientos técnicos señalaron a la extensión de navegador de Grammarly como principal causa, y David Bushell la instaló directamente en Mullvad browser, basado en Firefox, para comprobarlo
- Al instalar la extensión, los permisos incluyen lo siguiente
- acceso a los datos de todos los sitios web
- mostrar notificaciones
- acceso a las pestañas del navegador
- Grammarly inyecta en la página web una hoja de estilos cargada desde recursos locales de la extensión
- esta hoja de estilos no puede ser encontrada por la página mediante StyleSheetList
- también evade la Content Security Policy
- en Firefox, funciona como una hoja de estilos sigilosa que el propio sitio web difícilmente puede detectar
- La extensión agrega un elemento personalizado
<grammarly-desktop-integration> al documento <html> de todos los sitios web, incluso cuando el usuario no interactúa con ella
Cómo un solo nombre, --rem, rompió el layout
- Al final de la hoja de estilos de Grammarly se incluye el siguiente CSS
:host,
:root {
--rem:16
}
- En otras partes de esa misma hoja de estilos se usa
--rem para calcular el tamaño de fuente y la altura de línea
.kE2Bj {
font-size:calc(0.86px*(var(--rem) - 2));
line-height:calc(1.2868px*(var(--rem) - 2));
}
- El sitio también estaba usando la propiedad personalizada
--rem para sus propios experimentos de tipografía fluida
@layer base {
:root {
--rem: 0.0625rem;
--fluid: calc((100vi - (400 * var(--rem))) / (1920 - 400));
--font-size-h1: clamp(
calc(31 * var(--rem)),
calc((31 * var(--rem)) + (80 - 31) * var(--fluid)),
calc(80 * var(--rem))
);
}
}
- El
--rem del sitio estaba definido dentro de una cascade layer, y los estilos fuera de capas tienen prioridad sobre los que están dentro sin importar la especificidad de CSS
- el orden en el código fuente también influye, así que es posible que el
--rem de Grammarly haya ganado
- como resultado, las fórmulas de cálculo del sitio se rompieron y aparecieron problemas de layout
- Al principio respondieron detectando el web component agregado con un mutation observer y aplicando estilos con
!important
- Después de identificar la causa exacta, cambiaron el nombre de la propiedad personalizada del sitio a
--🤡
- ese nombre es una propiedad personalizada válida en CSS
--rem pasó a ser un nombre con riesgo de conflicto porque Grammarly lo usa de forma global
- Aunque Grammarly genera nombres de clase aleatorios, aplicó globalmente en
:root una propiedad personalizada tan común como --rem, e inyecta ese código en todas las páginas web aunque la extensión no se esté usando realmente
- Se pusieron en contacto con el equipo de soporte de Grammarly, pero todavía no han logrado llegar a una persona técnica que entienda el problema
1 comentarios
Opiniones en Hacker News
Mi experiencia con problemas de extensiones fue un poco distinta. Distribuimos una extensión que facilita cambiar de servidor proxy para pruebas de geolocalización.
Hace unos meses tuve la peor demo con un cliente: parecía que el producto simplemente no funcionaba. Después de depurar un buen rato, descubrimos que una actualización reciente de la extensión de 1Password había roto nuestra extensión. 1Password se suscribía al evento de autenticación, pero no devolvía nada, así que se agotaba el tiempo de espera y nuestro suscriptor nunca era llamado. Nuestra extensión le indicaba al navegador que cambiara el servidor proxy y luego quedaba lista para proporcionar las credenciales, pero la solicitud nunca llegaba. El soporte de 1Password fue mejor que el de Grammarly, pero es difícil convencer, a través del equipo de soporte, a un PM desconocido de que algo debe tener prioridad.
Después nos enteramos de que cierta extensión necesaria para sitios web del gobierno ruso tenía el mismo problema.
Como alguien que lleva más de 10 años trabajando con extensiones, al final Google tiene gran parte de la responsabilidad. Dejando de lado el tema político de los cambios a los bloqueadores de anuncios, Manifest v3 es, en muchos sentidos, mucho peor de lo esperado.
En general, siento que la calidad del código base de Chromium bajó bastante respecto de antes.
Si vas a inyectar scripts o estilos en páginas desconocidas, como mínimo deberías separar los espacios de nombres de las variables.
Pero el entrevistador lo descartó con un tono como de “las herramientas de hoy ya hacen eso y todo el mundo lo hace”. Tuve que estar de acuerdo en cierta medida, porque ya no trabajo en eso y, en realidad, no sé cómo está ahora. Pero resulta que no todo el mundo lo estaba haciendo.
Podíamos distinguir claramente lo que habíamos insertado nosotros de lo que ya estaba ahí, y evitar posibles conflictos.
Da miedo ver a ese intruso verde instalado por defecto en todos los sitios web durante una pantalla compartida o una grabación. No es solo que moleste visualmente: también trae problemas de privacidad y un vector de ataque evidente.
Chrome permite activar extensiones solo cuando hace falta; no entiendo por qué nadie lo hace así. Tampoco entiendo por qué ese no es el valor predeterminado en todos los navegadores.
Algunos colegas se sienten incómodos con la posibilidad de que la información pase a terceros, así que pausamos la reunión hasta que apaguen la extensión.
Soy ingeniero de Grammarly Extension. Antes que nada, lamento mucho que nuestra extensión haya roto la experiencia de usuario de dbushell.com y haya hecho que el autor gastara tiempo y esfuerzo en encontrar la causa.
No fue intencional, y usamos varias técnicas para evitar que pasen cosas así. Pero no fueron suficientes, y el artículo deja claro que hay margen para mejorar.
Como corrección rápida, agregamos una excepción temporal para dbushell.com. Al mismo tiempo, estamos trabajando en un cambio que garantice un aislamiento de estilos adecuado; este tipo de problemas nunca debería ocurrir.
Tengo un problema similar con Google Translate rompiendo mi app web. Los usuarios usan Google Translate y luego se quejan de que mi app se rompió, pero en realidad Google cambió el estado de la app en una capa meta superior. Es una práctica realmente mala.
Estoy intentando detectar Google Translate y mostrar una advertencia.
Por ejemplo, a veces hay que traducir una oración como “haz [clic aquí] para ver más información”. Al pasarla a otro idioma, puede hacer falta mover el enlace al final de la frase, algo como “para ver más información, haz [clic aquí]”. Para eso se necesita reacomodar elementos del DOM, y eso puede entrar en conflicto con apps interactivas.
Hay muchas cosas que el equipo de Google Translate podría hacer para reducir la interferencia, pero creo que sin una nueva API del navegador es difícil eliminarla por completo.
Ya se lo pasé al equipo de ingeniería.
Donde trabajo me vuelve loco que la gente no haga eso. Incluso el director de ingeniería agrega a su ticket cosas que tomarían menos tiempo simplemente resolver. Aun así, es buena señal que a menudo escuche: “no creé un ticket para enviar un mensaje; siguiendo tu método, le escribí directamente a esa persona”.
En la empresa tenemos muchos errores de Sentry causados porque las extensiones del navegador hacen cosas raras.
Google Translate de Chrome también es tristemente famoso por romper sitios basados en React.
Al final se vuelve un trabajo tedioso de clasificación: ir ignorando, una por una, las incidencias de nuevas extensiones. Para reducir el volumen de recolección usamos filtrado del lado del cliente. En general hay más ruido que en el backend, así que hay que poner umbrales mucho más altos.
No sorprende que haya muchos más errores en el frontend. Es porque hay que soportar muchas más variaciones de cliente que en un backend típico. Crear una app web grande que funcione bien para todo el mundo puede ser muy difícil.
Me pregunto cuál sería la única variable que, al inyectarla, podría romper más la web. Se me ocurre algo así:
--primary-color: transparent--serif: "Comic Sans MS"¿Cómo habría que lidiar con extensiones de navegador hostiles?
Mientras pensaba en esto abrí cualquier página de The Guardian con DevTools y vi que alguien había insertado un script y un iframe que apuntaban a twitter.com.
No me gusta Grammarly ni su modelo tecnológico, pero no es justo atribuir a la malicia algo que puede explicarse suficientemente por la estupidez.
Hace mucho que no trabajo en frontend, pero ¿no deberían tanto la extensión de Grammarly como el código propio usar nombres de atributos con espacio de nombres separado?
Me pregunto si se podría usar esto para secuestrar ese plugin. Como mínimo parece que se podría inyectar texto, y probablemente también renderizar un formulario de inicio de sesión bonito aprovechando la confianza que el usuario tiene en la extensión.
¿De verdad es seguro inyectar elementos en un documento controlado por otra persona?
Lo único que puedes hacer es imitar la UI de la extensión dentro del sitio web, pero para eso no hace falta inyectar nada. Basta con copiar el diseño.