- A causa de o site de David Bushell aparecer quebrado para alguns usuários por muito tempo era um CSS injetado às escondidas na página pela extensão de navegador do Grammarly
- No Firefox, a extensão do Grammarly injeta uma folha de estilos a partir de ativos locais da extensão, que é difícil de encontrar pela
StyleSheetList da página e também contorna a Content Security Policy
- O conflito aconteceu porque o Grammarly definiu globalmente
--rem:16 em :root, e o site usava o mesmo nome --rem para calcular sua tipografia fluida
- O
--rem do site estava dentro de uma cascade layer, e por causa da regra de CSS em que estilos fora da layer têm prioridade, o valor do Grammarly podia sobrescrever o cálculo
- Como medida temporária, ele resistiu com mutation observer e
!important, mas a resposta final foi mudar o nome da propriedade para --🤡; quando uma extensão injeta nomes comuns globalmente em :root, ela pode colidir facilmente com páginas da web
O CSS do Grammarly que entrou na página
- Durante meses, houve relatos esporádicos de que o layout do site saía do lugar e os tamanhos ficavam estranhos, acompanhados de capturas de tela
- Leitores com familiaridade técnica apontaram a extensão de navegador do Grammarly como principal causa, e David Bushell instalou diretamente no Mullvad browser baseado em Firefox para confirmar
- Ao instalar a extensão, as permissões incluíam:
- acesso aos dados de todos os sites
- exibição de notificações
- acesso às abas do navegador
- O Grammarly injeta na página web uma folha de estilos carregada a partir de ativos locais da extensão
- essa folha de estilos não pode ser encontrada pela StyleSheetList da página
- ela também contorna a Content Security Policy
- no Firefox, ela funciona como uma folha de estilos stealth que o próprio site dificilmente consegue detectar
- A extensão adiciona o elemento customizado
<grammarly-desktop-integration> ao documento <html> de todos os sites, mesmo sem interação do usuário
Como um único nome --rem quebrou o layout
- No fim da folha de estilos do Grammarly, havia o seguinte CSS
:host,
:root {
--rem:16
}
- Em outras partes da mesma folha de estilos,
--rem era usado para calcular o tamanho da fonte e a altura da linha
.kE2Bj {
font-size:calc(0.86px*(var(--rem) - 2));
line-height:calc(1.2868px*(var(--rem) - 2));
}
- O site também usava a propriedade customizada
--rem em seu próprio experimento de tipografia 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))
);
}
}
- O
--rem do site estava definido dentro de uma cascade layer, e estilos fora da layer têm prioridade sobre estilos dentro dela independentemente da specificity do CSS
- a ordem no código-fonte também influencia, então é possível que o
--rem do Grammarly tenha vencido
- como resultado, as fórmulas de cálculo do site quebraram e surgiram problemas de layout
- No início, a resposta foi detectar o web component adicionado com mutation observer e então aplicar estilos com
!important
- Depois de identificar a causa exata, o nome da propriedade customizada do site foi trocado para
--🤡
- esse nome é um nome de propriedade customizada válido em CSS
--rem passou a ser um nome com risco de colisão, já que o Grammarly o usa globalmente
- O Grammarly cria nomes de classe aleatórios, mas aplicou globalmente em
:root um nome de propriedade customizada genérico como --rem, além de injetar código em todas as páginas web mesmo quando a extensão não está sendo usada de fato
- A equipe de suporte do Grammarly foi contatada, mas ainda não foi possível chegar a um responsável técnico que entendesse o problema
1 comentários
Opiniões no Hacker News
O caso que tive com problemas de extensão foi um pouco diferente. Distribuímos uma extensão que facilita a troca de servidores proxy para testes de geolocalização.
Alguns meses atrás, fizemos a pior demonstração para cliente possível: parecia que o produto simplesmente não funcionava. Depois de muito depurar, descobrimos que uma atualização recente da extensão do 1Password tinha quebrado a nossa extensão. O 1Password se inscrevia em eventos de autenticação, mas não retornava, causando timeout; por isso, nosso assinante nunca era chamado. Nossa extensão mandava o navegador trocar o servidor proxy e ficava pronta para fornecer as credenciais, mas a solicitação nunca chegava. O suporte do 1Password foi melhor que o da Grammarly, mas é difícil convencer, via suporte, algum PM desconhecido a priorizar isso.
Depois descobrimos que alguma extensão necessária para sites do governo russo também tinha o mesmo problema.
Como alguém que trabalha com extensões há mais de 10 anos, no fim a responsabilidade do Google é grande. Deixando de lado a questão política das mudanças em bloqueadores de anúncios, o Manifest v3 é muito pior do que o esperado em vários aspectos.
No geral, sinto que a qualidade da base de código do Chromium caiu bastante em relação ao passado.
Se você injeta scripts ou estilos em páginas desconhecidas, deveria pelo menos separar o namespace das variáveis.
Mas o entrevistador meio que desdenhou, dizendo que hoje as ferramentas já fazem isso e todo mundo faz. Fui obrigado a concordar em certa medida, porque hoje não trabalho mais nisso e, na prática, não sei. Mas, pelo visto, nem todo mundo estava fazendo isso.
Dava para distinguir claramente o que tínhamos inserido do que já existia, e também evitávamos conflitos potenciais.
É assustador ver aquele intruso verde instalado por padrão em todos os sites durante compartilhamentos ou gravações de tela. Não é só um incômodo visual; há privacidade e vetores de ataque óbvios envolvidos.
No Chrome dá para ativar extensões só quando necessário, então não sei por que ninguém faz isso. Também me pergunto por que esse não é o padrão em todos os navegadores.
Alguns colegas se sentem desconfortáveis com a possibilidade de informações irem para terceiros, então suspendemos a reunião até que as extensões fossem desativadas.
Sou engenheiro da Grammarly Extension. Antes de tudo, peço sinceras desculpas por nossa extensão ter prejudicado a experiência do usuário em dbushell.com e por ter feito o autor gastar tempo e esforço para encontrar a causa.
Isso não foi intencional, e usamos várias técnicas para evitar que coisas assim aconteçam. Mas elas não foram suficientes, e o texto deixa claro que há espaço para melhorar.
Como correção rápida, adicionamos uma exceção temporária para dbushell.com. Ao mesmo tempo, estamos trabalhando em uma mudança que garanta o isolamento adequado de estilos; esse tipo de problema nunca deveria acontecer.
Tenho um problema parecido em que o Google Translate quebra meu web app. O usuário usa o Google Translate e reclama que meu app quebrou, mas, na verdade, foi o Google que alterou o estado do app em uma camada meta superior. É uma prática muito ruim.
Estou tentando detectar o Google Translate para exibir um alerta.
Por exemplo, às vezes é preciso traduzir uma frase como “clique [aqui] para ver mais informações”. Ao passar para outro idioma, pode ser necessário mover o link para o fim da frase, algo como “para ver mais informações, clique [aqui]”. Para fazer isso, é necessário reposicionar elementos do DOM, e isso pode entrar em conflito com apps interativos.
Há muita coisa que a equipe do Google Translate poderia fazer para reduzir a interferência, mas acho difícil eliminá-la completamente sem novas APIs de navegador.
Encaminhei para a equipe de engenharia.
Onde trabalho também fico maluco porque as pessoas não fazem isso. Até o diretor de engenharia adiciona ao próprio ticket coisas que levariam menos tempo simplesmente resolvendo. Ainda assim, é um bom sinal ouvir com frequência algo como: “não criei um ticket para mandar uma mensagem; fiz do seu jeito e mandei mensagem diretamente para a pessoa”.
Na empresa, há muitos erros do Sentry causados por extensões de navegador fazendo coisas estranhas.
O Google Translate do Chrome também é famoso por quebrar sites baseados em React.
No fim, vira um trabalho tedioso de triagem, ignorando um a um os problemas de novas extensões. Estamos usando filtragem no lado do cliente para reduzir o volume coletado. No geral, há muito mais ruído do que no backend, então é preciso definir limites bem mais altos.
Não surpreende que haja muito mais erros no frontend. É porque ele precisa dar suporte a muito mais variações de cliente do que um backend típico. Criar uma grande aplicação web que funcione bem para todo mundo pode ser muito difícil.
Fico imaginando qual seria uma única variável que, se injetada, conseguiria quebrar a web da forma mais ampla possível. Me vêm coisas assim:
--primary-color: transparent--serif: "Comic Sans MS"Como lidar com extensões de navegador hostis?
Pensando nisso, abri qualquer página do The Guardian no DevTools e vi que alguém tinha inserido scripts e iframes apontando para twitter.com.
Não gosto do Grammarly nem do modelo tecnológico dele, mas não é justo atribuir malícia a algo que pode ser suficientemente explicado por estupidez.
Faz tempo que não trabalho com frontend, mas a extensão do Grammarly e o próprio código não deveriam usar nomes de atributos com namespace separado?
Acho que daria para usar isso para sequestrar aquele plugin. No mínimo, parece possível injetar texto e, talvez, explorar a confiança que o usuário tem na extensão para renderizar um formulário de login bonitinho.
É realmente seguro injetar elementos em um documento controlado por outra pessoa?
O máximo que dá para fazer é imitar a UI da extensão dentro do site, mas isso nem exige injeção. Basta copiar o design.