2 पॉइंट द्वारा GN⁺ 2025-03-31 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • David Bushell की साइट कुछ यूज़र्स को लंबे समय तक टूटी हुई दिखने का कारण Grammarly browser extension द्वारा पेज में चुपके से inject किया गया CSS था
  • Firefox के हिसाब से Grammarly extension लोकल extension assets की एक stylesheet insert करता है, जिसे वेबपेज के StyleSheetList से ढूंढना मुश्किल है और जो Content Security Policy को भी bypass करता है
  • टकराव इसलिए हुआ क्योंकि Grammarly ने :root में globally defined --rem:16 रखा था और साइट की fluid typography गणना के लिए इस्तेमाल होने वाला --rem भी इसी नाम का था
  • साइट वाला --rem एक cascade layer के अंदर था, और CSS के उस नियम के कारण जिसमें layer के बाहर की styles को प्राथमिकता मिलती है, Grammarly की value गणना को override कर सकी
  • अस्थायी तौर पर mutation observer और !important से काम चलाया गया, लेकिन अंतिम उपाय property name को --🤡 में बदलना था; अगर कोई extension global :root में सामान्य नाम inject करे, तो वह वेबपेज से आसानी से टकरा सकता है

पेज के अंदर आया Grammarly CSS

  • कई महीनों तक साइट layout बिगड़ने और sizes अजीब दिखने की छिटपुट reports आईं, और साथ में screenshots भी भेजे गए
  • तकनीक से परिचित readers ने Grammarly browser extension को मुख्य कारण बताया, और David Bushell ने Firefox-आधारित Mullvad browser में इसे खुद install करके जांचा
  • extension install करते समय permissions में ये शामिल थे
    • सभी websites के data तक पहुंच
    • notifications दिखाना
    • browser tabs तक पहुंच
  • Grammarly वेबपेज में local extension assets से load होने वाली stylesheet inject करता है
    • यह stylesheet वेबपेज को StyleSheetList से नहीं मिलती
    • Content Security Policy को भी bypass करती है
    • Firefox के हिसाब से यह website के लिए detect करना मुश्किल stealth stylesheet की तरह काम करती है
  • extension, user के interact किए बिना भी, हर website के <html> document में <grammarly-desktop-integration> custom element जोड़ता है

कैसे --rem नाम ने layout तोड़ दिया

  • Grammarly stylesheet के अंत में यह CSS शामिल है
:host,
:root {
  --rem:16
}
  • उसी stylesheet के दूसरे हिस्सों में --rem का इस्तेमाल font size और line height calculate करने के लिए होता है
.kE2Bj {
  font-size:calc(0.86px*(var(--rem) - 2));
  line-height:calc(1.2868px*(var(--rem) - 2));
}
  • साइट भी अपने fluid typography experiment के लिए --rem custom property इस्तेमाल कर रही थी
@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 cascade layer के अंदर define था, और layer के बाहर की style, CSS specificity से परे, layer के अंदर की style से अधिक प्राथमिकता पाती है
    • source order भी असर डालता है, इसलिए संभव है कि Grammarly का --rem जीत गया हो
    • नतीजतन साइट की calculation expressions टूट गईं और layout समस्या हुई
  • शुरुआत में mutation observer से जोड़े गए web component को detect करने के बाद !important style जोड़कर workaround किया गया
  • सटीक कारण समझने के बाद साइट की custom property का नाम --🤡 में बदल दिया गया
    • यह नाम CSS में valid custom property name है
    • चूंकि Grammarly --rem को globally इस्तेमाल करता है, इसलिए यह collision risk वाला नाम बन गया
  • Grammarly random class names बनाते हुए भी --rem जैसे सामान्य custom property name को :root पर globally apply करता है, और extension को सच में इस्तेमाल न करने पर भी हर वेबपेज में code inject करता है
  • Grammarly support team से संपर्क किया गया है, लेकिन अभी तक समस्या समझने वाले technical प्रभारी तक बात नहीं पहुंची है

1 टिप्पणियां

 
GN⁺ 2025-03-31
Hacker News की राय
  • extension की समस्या से जुड़ा मेरा अनुभव थोड़ा अलग है। हम geo-location testing के लिए proxy server switching आसान बनाने वाला extension deploy करते हैं।
    कुछ महीने पहले हमने अब तक का सबसे खराब customer demo किया, जिसमें product ऐसा दिख रहा था जैसे वह बिल्कुल काम ही नहीं कर रहा हो। काफी देर debugging करने के बाद पता चला कि हाल के 1Password extension update ने हमारे extension को तोड़ दिया था। 1Password ने authentication event subscribe किया था, लेकिन उसे लौटाया नहीं, जिससे timeout हुआ और इसलिए हमारा subscriber call नहीं हुआ। हमारा extension browser को proxy server बदलने का निर्देश देने के बाद credentials देने के लिए तैयार था, लेकिन request आई ही नहीं। 1Password support team Grammarly से बेहतर थी, लेकिन support team के जरिए किसी अनजान PM को priority के लिए मनाना मुश्किल है।
    बाद में पता चला कि Russian government websites के लिए जरूरी एक extension में भी यही समस्या है।

    • मिलती-जुलती स्थिति है। 1Password अब भी दूसरे extensions के content scripts से Chrome side panel UI खोलने की functionality तोड़ता है। यह उस trust flag को बिगाड़ देता है जो बताता है कि event user interaction से आया था।
      10 साल से ज्यादा समय तक extensions side पर रहने के अनुभव से कहूं तो आखिरकार जिम्मेदारी काफी हद तक Google की है। ad blocker changes वाले political मुद्दे से अलग, Manifest v3 कई मायनों में उम्मीद से कहीं ज्यादा खराब है।
      कुल मिलाकर लगता है कि Chromium codebase की quality पहले की तुलना में काफी गिर गई है।
  • अगर आप किसी अनजान page में scripts या styles inject कर रहे हैं, तो कम-से-कम variable namespacing जरूर अलग रखें।

    • सच में गुस्सा इसी बात पर आता है कि करीब 5–6 महीने पहले एक interview में मैंने उस Instagram/branding startup की बात की जहां 2014 में मैं CTO और lead developer था। मैंने बताया था कि तब हमने build system बनाया था ताकि CSS classes और JavaScript objects की सही तरह से namespacing हो, conflict की कोई संभावना न रहे, और third-party customer sites पर कौन-से widgets हैं उसके आधार पर ठीक कौन-सी scripts load करनी हैं यह manage हो।
      लेकिन interviewer ने इसे यह कहकर dismiss कर दिया कि आजकल tools यह सब कर देते हैं और हर कोई ऐसा करता है। मुझे कुछ हद तक सहमत होना पड़ा, क्योंकि अब मैं वह काम नहीं करता और असल में नहीं जानता। लेकिन पता चला कि सभी लोग ऐसा कर भी नहीं रहे थे।
    • namespacing सिर्फ दूसरों के लिए नहीं, अपने लिए भी सुविधाजनक होती है। पिछली job में मैंने user-facing न होने वाला browser automation बनाया था; वह extension भी नहीं था, फिर भी namespacing उपयोगी रही।
      इससे हम साफ-साफ अलग कर पाते थे कि क्या हमने insert किया है और क्या पहले से था, और संभावित conflicts से भी बचते थे।
    • frontend field छोड़े हुए कुछ समय हो गया है; आजकल CSS namespacing आम तौर पर कैसे handle की जाती है?
    • इससे बेहतर है कि Shadow DOM इस्तेमाल करें।
  • screen sharing या recording में हर website पर default रूप से वह हरा intruder दिखता है, तो डर लगता है। बात सिर्फ visual annoyance की नहीं है; privacy और एक साफ attack vector भी साथ आते हैं।
    Chrome में जरूरत पड़ने पर ही extensions enable किए जा सकते हैं, फिर भी समझ नहीं आता कोई ऐसा क्यों नहीं करता। यह भी सवाल है कि सभी browsers में default यही क्यों नहीं है।

    • मुझे लगता है मैं काफी lucky हूं कि मेरे colleagues ऐसी चीजों की परवाह करते हैं। जब meeting participants में से कुछ के पास कोई खास extension या तरह-तरह के AI assistants install होना साफ दिखता है, तो हमने meetings रोक भी दी हैं।
      कुछ colleagues को यह असहज लगता है कि information किसी third party तक जा सकती है, इसलिए extension बंद होने तक meeting रोक दी जाती है।
  • मैं Grammarly Extension engineer हूं। सबसे पहले, हमारे extension ने dbushell.com के user experience को खराब किया और author को cause ढूंढने में time और effort लगाना पड़ा—इसके लिए सच में माफी चाहता हूं।
    यह जानबूझकर नहीं हुआ, और ऐसी चीजें न हों इसके लिए हम कई techniques इस्तेमाल करते हैं। लेकिन वे पर्याप्त नहीं थीं, और article से साफ है कि improvement की गुंजाइश है।
    quick fix के तौर पर हमने dbushell.com के लिए temporary exception जोड़ दिया है। साथ ही हम ऐसी change पर काम कर रहे हैं जो सही style isolation सुनिश्चित करेगी, और ऐसी समस्या कभी नहीं होनी चाहिए।

  • Google Translate मेरे web app को तोड़ देता है—ऐसी ही समस्या है। users Google Translate इस्तेमाल करते हुए शिकायत करते हैं कि मेरा app टूट गया है, लेकिन असल में Google ने ऊंचे meta layer पर app state बदल दी होती है। यह सच में खराब practice है।
    मैं Google Translate detect करके warning दिखाने की कोशिश कर रहा हूं।

    • यह दो दिन पहले वाले case से जुड़ा हो सकता है: https://www.pewresearch.org/decoded/2025/03/21/how-a-glitch-... / https://news.ycombinator.com/item?id=43441880
    • Google Translate का हस्तक्षेप परेशान करता है, लेकिन मौजूदा browser tools के साथ मुझे लगता है कि इसका अलग तरीके से काम करना मुश्किल है।
      उदाहरण के लिए, कभी-कभी “अधिक जानकारी देखने के लिए [यहां क्लिक करें]” जैसी sentence translate करनी होती है। दूसरी भाषा में ले जाते समय link को sentence के अंत में ले जाकर “[यहां क्लिक करें] to see more information” जैसा बनाना पड़ सकता है। इसके लिए DOM element rearrangement चाहिए, और यह interactive apps से टकरा सकता है।
      Google Translate team interference घटाने के लिए बहुत कुछ कर सकती है, लेकिन नए browser API के बिना इसे पूरी तरह हटाना मुश्किल लगता है।
  • Engineering team को forward कर दिया है।

    • ऐसे one-line fixes का backlog hell में लंबे समय तक पड़े रहना काफी खटकता है। मैं ऐसी company चाहता हूं जहां developer कहे, “ticket लिखने से जल्दी अभी fix करना है, तो बस कर देते हैं।”
      जहां मैं काम करता हूं, वहां लोग ऐसा नहीं करते और मैं पागल हो जाता हूं। Engineering director तक ऐसी चीज को अपने ticket में add कर देता है जिसमें सीधे handle करने से कम time लगेगा। फिर भी अक्सर यह सुनना कि “मैंने message भेजने के लिए ticket नहीं बनाया, आपकी तरह सीधे उस व्यक्ति को message कर दिया” एक अच्छा संकेत है।
  • कंपनी में browser extensions की अजीब हरकतों की वजह से आने वाली Sentry errors बहुत होती हैं
    Chrome का Google Translate भी React-आधारित साइटों को तोड़ने के लिए बदनाम है
    आखिर में यह नए extension issues को एक-एक करके ignore करने वाला उबाऊ triage काम बन जाता है। collection volume घटाने के लिए client-side filtering इस्तेमाल कर रहे हैं। कुल मिलाकर backend की तुलना में noise ज़्यादा होता है, इसलिए threshold काफी ऊँची रखनी पड़ती है

    • यह सिर्फ साधारण noise नहीं है। असल में users को इसकी वजह से crashes या दूसरी समस्याएँ हो रही हैं। Google Translate extension का React और दूसरे web apps में interference पर विस्तार से लिखा एक लेख है: https://martijnhols.nl/blog/everything-about-google-translat...
      frontend में errors बहुत ज़्यादा होना चौंकाने वाला नहीं है। क्योंकि इसे सामान्य backend की तुलना में कहीं ज़्यादा client variations support करने पड़ते हैं। ऐसा बड़ा web app बनाना जो सबके लिए ठीक चले, बहुत मुश्किल हो सकता है
    • क्या आप “Object captured as exception” error की बात कर रहे हैं? अगर वही error है जिसमें Sentry कोई guidance नहीं देता, तो हम उसे बस client-side पर filter कर देते हैं
  • सोच रहा हूँ कि अगर web को सबसे ज़्यादा तोड़ सकने वाला कोई एक variable inject करना हो, तो वह क्या होगा। कुछ ऐसा ध्यान आता है:
    --primary-color: transparent

    • --serif: "Comic Sans MS"
  • hostile browser extensions से कैसे निपटना चाहिए?

    • मेरे चलाए community website पर मेरी पसंदीदा शिकायत यही है। “Ad page पर photos नहीं दिख रहे।” क्या आप ad blocker इस्तेमाल कर रहे हैं? “हाँ।” आपको क्या लगता है ad blocker क्या करता है...
    • page DOM की valid state define करके, page loading खत्म होने के कुछ seconds बाद “hostile” elements और CSS styles scan करके delete भी किए जा सकते हैं
      यह सोचते हुए मैंने The Guardian का कोई भी page DevTools में खोला, तो किसी ने twitter.com की ओर इशारा करने वाली script और iframe insert कर रखे थे
    • इस मामले में ‘hostile’ कहना मुझे थोड़ा ज़्यादा लगता है। ‘competence की कमी’ कहना काफी है। बस syllables ज़्यादा हो जाते हैं
      मुझे Grammarly या उसका technology model पसंद नहीं है, लेकिन जिस चीज़ को मूर्खता से ठीक-ठीक समझाया जा सकता हो, उसमें दुर्भावना ठहराना fair नहीं है
      frontend काम किए काफी समय हो गया है, लेकिन क्या Grammarly extension और आपके code दोनों को namespaced property names इस्तेमाल नहीं करने चाहिए?
    • इस point पर मैं browser extensions install ही नहीं करता
    • उसे remove कर दें तो नहीं चलेगा?
  • लगता है इसका इस्तेमाल करके उस plugin को hijack किया जा सकता है। कम से कम text inject तो किया ही जा सकता है, और शायद user का extension पर जो भरोसा है उसका फायदा उठाकर एक सुंदर login form भी render किया जा सकता है
    किसी और के control वाले document में elements inject करना सच में safe है?

    • समझ नहीं आ रहा यह कैसे काम करेगा। वे आपके page में CSS inject करते हैं, लेकिन website extension UI में कुछ inject नहीं कर सकती
      आप बस website के अंदर extension UI की नकल कर सकते हैं, और उसके लिए injection की ज़रूरत नहीं है। बस design copy कर लीजिए