2 poin oleh GN⁺ 2025-03-31 | 1 komentar | Bagikan ke WhatsApp
  • Penyebab situs David Bushell tampak rusak bagi sebagian pengguna sejak lama ternyata adalah CSS yang disuntikkan secara diam-diam ke halaman oleh ekstensi browser Grammarly
  • Di Firefox, ekstensi Grammarly menyisipkan stylesheet dari aset ekstensi lokal, sulit ditemukan lewat StyleSheetList milik halaman web, dan juga melewati Content Security Policy
  • Konflik terjadi karena Grammarly mendefinisikan --rem:16 secara global pada :root, sementara situs juga memakai nama yang sama, --rem, untuk perhitungan tipografi fluid
  • --rem di sisi situs berada di dalam cascade layer, dan karena aturan CSS membuat style di luar layer lebih diprioritaskan, nilai Grammarly dapat menimpa perhitungan tersebut
  • Sementara sempat bertahan dengan mutation observer dan !important, tetapi respons akhirnya adalah mengganti nama properti menjadi --🤡; jika ekstensi menyuntikkan nama biasa ke :root global, ia mudah berbenturan dengan halaman web

CSS Grammarly yang masuk ke dalam halaman

  • Selama beberapa bulan ada laporan sporadis bahwa tata letak situs bergeser dan ukurannya aneh, disertai tangkapan layar
  • Pembaca yang akrab dengan teknologi menunjuk Grammarly browser extension sebagai penyebab utama, dan David Bushell memastikannya dengan memasangnya langsung di Mullvad browser berbasis Firefox
  • Saat ekstensi dipasang, izinnya mencakup hal berikut
    • Akses ke data semua situs web
    • Menampilkan notifikasi
    • Akses ke tab browser
  • Grammarly menyuntikkan stylesheet yang dimuat dari aset ekstensi lokal ke halaman web
    • Stylesheet ini tidak dapat ditemukan oleh halaman web lewat StyleSheetList
    • Ia juga melewati Content Security Policy
    • Di Firefox, ia bertindak seperti stylesheet siluman yang sulit dideteksi oleh situs web itu sendiri
  • Ekstensi menambahkan elemen kustom <grammarly-desktop-integration> ke dokumen <html> di semua situs web, bahkan tanpa interaksi pengguna

Bagaimana satu nama --rem merusak tata letak

  • Di akhir stylesheet Grammarly terdapat CSS berikut
:host,
:root {
  --rem:16
}
  • Di bagian lain stylesheet yang sama, --rem digunakan untuk menghitung ukuran font dan tinggi baris
.kE2Bj {
  font-size:calc(0.86px*(var(--rem) - 2));
  line-height:calc(1.2868px*(var(--rem) - 2));
}
  • Situs tersebut juga memakai custom property --rem untuk eksperimen tipografi fluid miliknya sendiri
@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))
    );
  }
}
  • --rem milik situs didefinisikan di dalam cascade layer, dan style di luar layer diprioritaskan dibanding style di dalam layer tanpa memandang CSS specificity
    • Urutan sumber juga berpengaruh, sehingga ada kemungkinan --rem milik Grammarly yang menang
    • Akibatnya, rumus perhitungan situs rusak dan muncul masalah tata letak
  • Awalnya, ia menanganinya dengan mendeteksi web component yang ditambahkan lewat mutation observer, lalu menambahkan style !important
  • Setelah penyebab pastinya dipahami, nama custom property situs diganti menjadi --🤡
    • Nama ini adalah nama custom property yang valid di CSS
    • Karena --rem dipakai Grammarly secara global, nama itu menjadi berisiko bentrok
  • Meski membuat nama class acak, Grammarly menerapkan nama custom property umum --rem secara global ke :root, dan menyuntikkan kode ke semua halaman web bahkan ketika ekstensi tidak benar-benar digunakan
  • Tim dukungan Grammarly telah dihubungi, tetapi sejauh ini belum berhasil tersambung dengan staf teknis yang memahami masalah tersebut

1 komentar

 
GN⁺ 2025-03-31
Pendapat Hacker News
  • Kasus yang saya alami dengan masalah ekstensi sedikit berbeda. Kami mendistribusikan ekstensi yang memudahkan beralih server proxy untuk pengujian geolokasi.
    Beberapa bulan lalu saya melakukan demo pelanggan terburuk: produk kami terlihat seperti sama sekali tidak berfungsi. Setelah cukup lama debugging, kami menemukan bahwa pembaruan terbaru ekstensi 1Password merusak ekstensi kami. 1Password berlangganan event autentikasi tetapi tidak mengembalikannya, sehingga terjadi timeout, dan subscriber kami tidak pernah dipanggil. Ekstensi kami sudah memberi instruksi ke browser untuk mengubah server proxy lalu bersiap menyediakan kredensial, tetapi permintaannya tidak pernah datang. Dukungan 1Password lebih baik daripada Grammarly, tetapi sulit meyakinkan PM yang tidak dikenal soal prioritas lewat tim dukungan.
    Setelah itu kami mengetahui bahwa sebuah ekstensi yang diperlukan untuk situs web pemerintah Rusia juga mengalami masalah yang sama.

    • Situasinya mirip. 1Password masih merusak kemampuan membuka UI side panel Chrome dari content script ekstensi lain. Ia merusak flag tepercaya yang menunjukkan bahwa event berasal dari interaksi pengguna.
      Sebagai orang yang sudah berkecimpung di ekstensi selama lebih dari 10 tahun, pada akhirnya Google punya tanggung jawab besar. Terlepas dari isu politis perubahan pemblokir iklan, Manifest v3 dalam banyak hal jauh lebih buruk daripada yang diharapkan.
      Secara umum, kualitas codebase Chromium terasa jauh menurun dibanding dulu.
  • Kalau Anda menyuntikkan script atau style ke halaman yang tidak dikenal, setidaknya namespace nama variabel harus dipisahkan.

    • Yang benar-benar membuat kesal adalah, sekitar 5–6 bulan lalu dalam wawancara saya bercerita tentang startup Instagram/branding tempat saya bekerja sebagai CTO sekaligus developer utama pada 2014. Saya menjelaskan bahwa saat itu saya membuat build system agar class CSS dan objek JavaScript benar-benar dipisahkan namespace-nya, memastikan tidak ada kemungkinan bentrok, serta mengelola dengan tepat script mana yang harus dimuat tergantung widget apa yang ada di situs pelanggan pihak ketiga.
      Namun pewawancara menepisnya seolah-olah itu semua sudah dilakukan tooling modern dan semua orang melakukannya. Saya mau tidak mau agak setuju, karena saya sekarang tidak lagi mengerjakan hal itu sehingga tidak benar-benar tahu. Tetapi ternyata tidak semua orang melakukannya.
    • Pemisahan namespace bukan hanya untuk orang lain, tetapi juga memudahkan diri sendiri. Di pekerjaan sebelumnya, kami membuat otomasi browser yang tidak terekspos ke pengguna; meski bukan ekstensi, pemisahan namespace tetap berguna.
      Kami bisa membedakan dengan jelas mana yang kami sisipkan dan mana yang sudah ada, sekaligus menghindari potensi bentrokan.
    • Saya sudah cukup lama meninggalkan bidang frontend; sekarang pemisahan namespace CSS biasanya ditangani dengan cara apa?
    • Lebih baik lagi, gunakan Shadow DOM.
  • Menyeramkan melihat penyusup hijau itu terpasang secara default di semua situs web saat screen sharing atau rekaman. Bukan hanya mengganggu secara visual; ada implikasi privasi dan vektor serangan yang jelas.
    Di Chrome, ekstensi bisa dinyalakan hanya saat diperlukan, jadi saya tidak tahu kenapa tidak ada yang melakukannya. Saya juga heran kenapa itu bukan default di semua browser.

    • Saya merasa cukup beruntung punya rekan kerja yang peduli soal hal-hal seperti ini. Kami pernah menghentikan rapat ketika terlihat jelas sebagian peserta memasang ekstensi tertentu atau berbagai jenis asisten AI.
      Beberapa rekan tidak nyaman dengan kemungkinan informasi mengalir ke pihak ketiga, jadi rapat dihentikan sampai ekstensi dimatikan.
  • Saya engineer Grammarly Extension. Pertama-tama, kami benar-benar minta maaf karena ekstensi kami merusak pengalaman pengguna di dbushell.com dan membuat penulis menghabiskan waktu serta tenaga untuk menemukan penyebabnya.
    Itu bukan sesuatu yang kami niatkan, dan kami menggunakan berbagai teknik untuk mencegah hal seperti ini terjadi. Namun itu belum cukup, dan tulisan tersebut jelas menunjukkan masih ada ruang untuk perbaikan.
    Sebagai perbaikan cepat, kami menambahkan pengecualian sementara untuk dbushell.com. Pada saat yang sama, kami sedang mengerjakan perubahan untuk memastikan isolasi style yang tepat; masalah seperti ini seharusnya sama sekali tidak terjadi.

  • Saya punya masalah serupa: Google Translate merusak aplikasi web saya. Pengguna memakai Google Translate lalu mengeluh aplikasi saya rusak, padahal sebenarnya Google mengubah state aplikasi di lapisan meta yang lebih tinggi. Ini praktik yang sangat buruk.
    Saya sedang mencoba mendeteksi Google Translate dan menampilkan peringatan.

    • Mungkin terkait dengan kasus dua hari lalu: https://www.pewresearch.org/decoded/2025/03/21/how-a-glitch-... / https://news.ycombinator.com/item?id=43441880
    • Intervensi Google Translate memang menyebalkan, tetapi dengan tool browser saat ini, saya rasa sulit baginya bekerja dengan cara lain.
      Misalnya, kadang perlu menerjemahkan kalimat seperti “Klik [di sini] untuk melihat informasi lebih lanjut.” Ketika dipindahkan ke bahasa lain, link mungkin perlu dipindah ke akhir kalimat menjadi “Untuk melihat informasi lebih lanjut, klik [di sini].” Untuk melakukan ini diperlukan penataan ulang elemen DOM, dan itu bisa berbenturan dengan aplikasi interaktif.
      Ada banyak hal yang bisa dilakukan tim Google Translate untuk mengurangi intervensi, tetapi menurut saya sulit menghilangkannya sepenuhnya tanpa API browser baru.
  • Sudah saya teruskan ke tim engineering.

    • Cukup menjengkelkan melihat perbaikan satu baris seperti ini lama teronggok di neraka backlog. Saya ingin perusahaan tempat developer berkata, “Lebih cepat diperbaiki sekarang daripada menulis tiket, jadi langsung saja.”
      Di tempat kerja saya pun saya dibuat gila karena orang-orang tidak melakukannya. Bahkan direktur engineering menambahkan ke tiketnya sendiri sesuatu yang sebenarnya memakan waktu lebih sedikit daripada langsung dikerjakan. Meski begitu, sering mendengar kalimat “Saya langsung mengirim pesan ke orang itu sesuai cara Anda, tanpa membuat tiket untuk mengirim pesan” adalah pertanda baik.
  • Di perusahaan, ada banyak error Sentry yang muncul karena ekstensi browser melakukan hal-hal aneh
    Google Translate di Chrome juga terkenal merusak situs berbasis React
    Pada akhirnya ini menjadi pekerjaan klasifikasi yang membosankan: mengabaikan satu per satu masalah ekstensi baru. Kami memakai filtering di sisi klien untuk mengurangi volume yang terkumpul. Secara keseluruhan, karena noise-nya lebih banyak daripada backend, ambang batasnya harus dibuat jauh lebih tinggi

    • Ini bukan sekadar noise. Pengguna memang benar-benar mengalami crash atau masalah lain karenanya. Ada tulisan yang membahas detail intervensi ekstensi Google Translate terhadap React dan aplikasi web lain: https://martijnhols.nl/blog/everything-about-google-translat...
      Tidak mengejutkan kalau frontend punya jauh lebih banyak error. Sebab ia harus mendukung variasi klien yang jauh lebih banyak dibanding backend pada umumnya. Membuat aplikasi web besar yang berjalan baik untuk semua orang bisa sangat sulit
    • Maksudnya error “Object captured as exception”? Kalau itu error yang sama sekali tidak diberi panduan oleh Sentry, kami langsung memfilternya di sisi klien
  • Saya penasaran, kalau menyuntikkan satu variabel yang bisa paling besar merusak web, apa itu? Yang terpikir seperti ini:
    --primary-color: transparent

    • --serif: "Comic Sans MS"
  • Bagaimana cara menghadapi ekstensi browser yang bersifat adversarial?

    • Ini keluhan favorit saya di situs komunitas yang saya kelola. “Foto di halaman iklan tidak terlihat.” Anda pakai ad blocker? “Ya.” Menurut Anda ad blocker itu melakukan apa...
    • Sepertinya bisa saja mendefinisikan state DOM halaman yang valid, lalu beberapa detik setelah halaman selesai dimuat, memindai elemen dan style CSS yang “adversarial” lalu menghapusnya
      Sambil memikirkan ini, saya membuka halaman acak The Guardian dengan DevTools, dan ternyata ada seseorang menyisipkan script dan iframe yang mengarah ke twitter.com
    • Dalam kasus ini, menurut saya istilah ‘adversarial’ agak berlebihan. ‘Kurang kompeten’ sudah cukup. Hanya saja jumlah suku katanya lebih panjang
      Saya tidak menyukai Grammarly maupun model teknologinya, tetapi tidak adil memberi label niat jahat pada sesuatu yang sudah cukup dijelaskan oleh kebodohan
      Sudah lama sejak saya mengerjakan frontend, tetapi bukankah ekstensi Grammarly dan kode mereka sendiri seharusnya sama-sama memakai nama atribut yang dipisahkan namespace?
    • Pada titik ini saya sama sekali tidak memasang ekstensi browser
    • Bukankah tinggal dihapus saja?
  • Saya merasa ini bisa dipakai untuk membajak plugin itu. Setidaknya sepertinya bisa menyuntikkan teks, dan mungkin juga merender form login yang rapi dengan memanfaatkan kepercayaan pengguna terhadap ekstensi tersebut
    Apakah benar-benar aman menyisipkan elemen ke dokumen yang dikendalikan orang lain?

    • Saya tidak mengerti bagaimana itu akan bekerja. Mereka menyuntikkan CSS ke halaman Anda, tetapi situs web tidak bisa menyuntikkan sesuatu ke UI ekstensi
      Yang bisa dilakukan hanyalah meniru UI ekstensi di dalam situs web, dan itu tidak perlu injeksi. Cukup tiru saja desainnya