- 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 टिप्पणियां
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 में भी यही समस्या है।
10 साल से ज्यादा समय तक extensions side पर रहने के अनुभव से कहूं तो आखिरकार जिम्मेदारी काफी हद तक Google की है। ad blocker changes वाले political मुद्दे से अलग, Manifest v3 कई मायनों में उम्मीद से कहीं ज्यादा खराब है।
कुल मिलाकर लगता है कि Chromium codebase की quality पहले की तुलना में काफी गिर गई है।
अगर आप किसी अनजान page में scripts या styles inject कर रहे हैं, तो कम-से-कम variable namespacing जरूर अलग रखें।
लेकिन interviewer ने इसे यह कहकर dismiss कर दिया कि आजकल tools यह सब कर देते हैं और हर कोई ऐसा करता है। मुझे कुछ हद तक सहमत होना पड़ा, क्योंकि अब मैं वह काम नहीं करता और असल में नहीं जानता। लेकिन पता चला कि सभी लोग ऐसा कर भी नहीं रहे थे।
इससे हम साफ-साफ अलग कर पाते थे कि क्या हमने insert किया है और क्या पहले से था, और संभावित conflicts से भी बचते थे।
screen sharing या recording में हर website पर default रूप से वह हरा intruder दिखता है, तो डर लगता है। बात सिर्फ visual annoyance की नहीं है; privacy और एक साफ attack vector भी साथ आते हैं।
Chrome में जरूरत पड़ने पर ही extensions enable किए जा सकते हैं, फिर भी समझ नहीं आता कोई ऐसा क्यों नहीं करता। यह भी सवाल है कि सभी browsers में default यही क्यों नहीं है।
कुछ 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 दिखाने की कोशिश कर रहा हूं।
उदाहरण के लिए, कभी-कभी “अधिक जानकारी देखने के लिए [यहां क्लिक करें]” जैसी sentence translate करनी होती है। दूसरी भाषा में ले जाते समय link को sentence के अंत में ले जाकर “[यहां क्लिक करें] to see more information” जैसा बनाना पड़ सकता है। इसके लिए DOM element rearrangement चाहिए, और यह interactive apps से टकरा सकता है।
Google Translate team interference घटाने के लिए बहुत कुछ कर सकती है, लेकिन नए browser API के बिना इसे पूरी तरह हटाना मुश्किल लगता है।
Engineering team को forward कर दिया है।
जहां मैं काम करता हूं, वहां लोग ऐसा नहीं करते और मैं पागल हो जाता हूं। 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 काफी ऊँची रखनी पड़ती है
frontend में errors बहुत ज़्यादा होना चौंकाने वाला नहीं है। क्योंकि इसे सामान्य backend की तुलना में कहीं ज़्यादा client variations support करने पड़ते हैं। ऐसा बड़ा web app बनाना जो सबके लिए ठीक चले, बहुत मुश्किल हो सकता है
सोच रहा हूँ कि अगर web को सबसे ज़्यादा तोड़ सकने वाला कोई एक variable inject करना हो, तो वह क्या होगा। कुछ ऐसा ध्यान आता है:
--primary-color: transparent--serif: "Comic Sans MS"hostile browser extensions से कैसे निपटना चाहिए?
यह सोचते हुए मैंने The Guardian का कोई भी page DevTools में खोला, तो किसी ने twitter.com की ओर इशारा करने वाली script और iframe insert कर रखे थे
मुझे Grammarly या उसका technology model पसंद नहीं है, लेकिन जिस चीज़ को मूर्खता से ठीक-ठीक समझाया जा सकता हो, उसमें दुर्भावना ठहराना fair नहीं है
frontend काम किए काफी समय हो गया है, लेकिन क्या Grammarly extension और आपके code दोनों को namespaced property names इस्तेमाल नहीं करने चाहिए?
लगता है इसका इस्तेमाल करके उस plugin को hijack किया जा सकता है। कम से कम text inject तो किया ही जा सकता है, और शायद user का extension पर जो भरोसा है उसका फायदा उठाकर एक सुंदर login form भी render किया जा सकता है
किसी और के control वाले document में elements inject करना सच में safe है?
आप बस website के अंदर extension UI की नकल कर सकते हैं, और उसके लिए injection की ज़रूरत नहीं है। बस design copy कर लीजिए