- Nguyên nhân khiến trang của David Bushell trông bị vỡ trong thời gian dài với một số người dùng là do tiện ích mở rộng trình duyệt Grammarly bí mật chèn CSS vào trang
- Trên Firefox, tiện ích Grammarly chèn stylesheet từ tài nguyên mở rộng cục bộ, khó bị trang web phát hiện qua
StyleSheetList và còn vượt qua cả Content Security Policy
- Xung đột xảy ra vì Grammarly định nghĩa toàn cục
--rem:16 trên :root, trong khi trang cũng dùng --rem cho phép tính typography linh hoạt với cùng tên
--rem phía trang nằm bên trong cascade layer, và do quy tắc CSS rằng style ngoài layer được ưu tiên hơn, giá trị của Grammarly có thể ghi đè lên phép tính
- Tạm thời tác giả cầm cự bằng mutation observer và
!important, nhưng cách xử lý cuối cùng là đổi tên thuộc tính thành --🤡; nếu tiện ích chèn những tên thông thường vào :root toàn cục thì rất dễ xung đột với trang web
CSS của Grammarly đã đi vào bên trong trang
- Trong nhiều tháng đã có những báo cáo lẻ tẻ rằng bố cục trang bị lệch và kích thước hiển thị kỳ lạ, kèm theo cả ảnh chụp màn hình
- Những độc giả am hiểu kỹ thuật đã chỉ ra Grammarly browser extension là nguyên nhân chính, và David Bushell đã tự cài nó trên Mullvad browser dựa trên Firefox để kiểm chứng
- Khi cài tiện ích, các quyền bao gồm
- truy cập dữ liệu của mọi website
- hiển thị thông báo
- truy cập tab trình duyệt
- Grammarly chèn vào trang web một stylesheet được tải từ tài nguyên mở rộng cục bộ
- stylesheet này không thể bị trang web tìm thấy qua StyleSheetList
- nó cũng vượt qua Content Security Policy
- trên Firefox, nó hoạt động như một stylesheet tàng hình mà bản thân website rất khó phát hiện
- Tiện ích còn thêm phần tử tùy biến
<grammarly-desktop-integration> vào tài liệu <html> của mọi website ngay cả khi người dùng không hề tương tác
Chỉ một cái tên --rem đã làm hỏng bố cục như thế nào
- Ở cuối stylesheet của Grammarly có đoạn CSS sau
:host,
:root {
--rem:16
}
- Ở các phần khác của cùng stylesheet,
--rem được dùng để tính cỡ chữ và chiều cao dòng
.kE2Bj {
font-size:calc(0.86px*(var(--rem) - 2));
line-height:calc(1.2868px*(var(--rem) - 2));
}
- Bản thân trang cũng đang dùng thuộc tính tùy biến
--rem cho thử nghiệm typography linh hoạt của mình
@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 của trang được định nghĩa bên trong cascade layer, còn style ngoài layer thì được ưu tiên hơn style trong layer bất kể CSS specificity
- thứ tự nguồn cũng có ảnh hưởng, nên
--rem của Grammarly có thể đã thắng
- kết quả là các công thức tính của trang bị phá vỡ và gây ra lỗi bố cục
- Ban đầu, tác giả đối phó bằng cách dùng mutation observer để phát hiện web component được thêm vào rồi áp thêm style
!important
- Sau khi xác định đúng nguyên nhân, tác giả đổi tên thuộc tính tùy biến của trang thành
--🤡
- đây là một tên thuộc tính tùy biến hợp lệ trong CSS
--rem trở thành một cái tên có nguy cơ xung đột vì Grammarly dùng nó ở phạm vi toàn cục
- Grammarly tạo ra tên class ngẫu nhiên nhưng lại áp một tên thuộc tính tùy biến rất phổ biến là
--rem toàn cục trên :root, đồng thời chèn mã vào mọi trang web ngay cả khi người dùng không thực sự dùng tiện ích
- Tác giả đã liên hệ với bộ phận hỗ trợ của Grammarly, nhưng hiện vẫn chưa gặp được người phụ trách kỹ thuật hiểu rõ vấn đề
1 bình luận
Ý kiến trên Hacker News
Trường hợp gặp phải do vấn đề extension của tôi hơi khác một chút. Chúng tôi đang phát hành một extension giúp dễ dàng chuyển đổi máy chủ proxy để kiểm thử vị trí địa lý.
Vài tháng trước, chúng tôi có buổi demo khách hàng tệ hại nhất: sản phẩm trông như thể hoàn toàn không hoạt động. Sau một hồi debug, chúng tôi phát hiện bản cập nhật gần đây của extension 1Password đã làm hỏng extension của chúng tôi. 1Password đã subscribe sự kiện xác thực nhưng không trả về, dẫn đến timeout, nên subscriber của chúng tôi không được gọi. Extension của chúng tôi đã yêu cầu trình duyệt đổi máy chủ proxy rồi sẵn sàng cung cấp thông tin xác thực, nhưng request lại không bao giờ đến. Đội hỗ trợ 1Password khá hơn Grammarly, nhưng rất khó thuyết phục một PM không rõ là ai ưu tiên vấn đề thông qua đội hỗ trợ.
Sau đó chúng tôi biết được rằng một extension nào đó cần cho các trang web chính phủ Nga cũng gặp cùng vấn đề.
Với tư cách người đã làm về extension hơn 10 năm, rốt cuộc Google chịu trách nhiệm lớn. Bỏ qua vấn đề chính trị quanh thay đổi đối với ad blocker, Manifest v3 ở nhiều khía cạnh tệ hơn kỳ vọng rất nhiều.
Nhìn chung, tôi có cảm giác chất lượng codebase Chromium đã giảm khá nhiều so với trước.
Nếu bạn tiêm script hoặc style vào một trang không biết trước, tối thiểu cũng phải tách namespace cho biến.
Nhưng người phỏng vấn lại gạt đi kiểu như những thứ đó giờ công cụ đều làm hết rồi và ai cũng làm vậy. Tôi phần nào buộc phải đồng ý, vì giờ tôi không còn làm việc đó nên thực tế cũng không biết. Nhưng hóa ra không phải ai cũng làm như vậy.
Chúng tôi có thể phân biệt rõ cái gì do mình chèn vào với cái vốn đã có, đồng thời tránh được các xung đột tiềm ẩn.
Thật đáng sợ khi thấy kẻ xâm nhập màu xanh đó được cài mặc định trên mọi website trong các buổi chia sẻ màn hình hay ghi hình. Không chỉ là vấn đề gây khó chịu về mặt thị giác, mà còn kéo theo quyền riêng tư và một vector tấn công rõ ràng.
Chrome có thể chỉ bật extension khi cần, nhưng tôi không hiểu vì sao không ai làm vậy. Tôi cũng tự hỏi vì sao đó không phải là mặc định của mọi trình duyệt.
Một số đồng nghiệp không thoải mái với khả năng thông tin bị chuyển cho bên thứ ba, nên họ tạm dừng cuộc họp cho đến khi extension được tắt.
Tôi là kỹ sư của Grammarly Extension. Trước hết, chúng tôi thành thật xin lỗi vì extension của chúng tôi đã làm hỏng trải nghiệm người dùng trên dbushell.com và khiến tác giả phải tốn thời gian, công sức để tìm nguyên nhân.
Đây không phải điều chúng tôi mong muốn, và chúng tôi đang dùng nhiều kỹ thuật để tránh chuyện như vậy xảy ra. Nhưng rõ ràng như vậy là chưa đủ, và bài viết cho thấy có nhiều điểm cần cải thiện.
Như một bản sửa nhanh, chúng tôi đã thêm ngoại lệ tạm thời cho dbushell.com. Đồng thời, chúng tôi đang làm một thay đổi để bảo đảm cô lập style phù hợp; những vấn đề như thế này tuyệt đối không nên xảy ra.
Tôi gặp vấn đề tương tự khi Google Translate làm hỏng web app của mình. Người dùng dùng Google Translate rồi phàn nàn rằng app của tôi bị lỗi, nhưng thực tế là Google đã thay đổi trạng thái app ở một tầng meta cao hơn. Đây thật sự là một thực hành rất tệ.
Tôi đang cố phát hiện Google Translate để hiển thị cảnh báo.
Ví dụ, đôi khi cần dịch một câu như “nhấp [vào đây] để xem thêm thông tin”. Khi chuyển sang ngôn ngữ khác, có thể phải đưa liên kết về cuối câu, thành kiểu “để xem thêm thông tin, hãy [nhấp vào đây]”. Để làm vậy cần sắp xếp lại các phần tử DOM, và việc này có thể xung đột với ứng dụng tương tác.
Đội Google Translate có thể làm nhiều việc để giảm can thiệp, nhưng tôi nghĩ sẽ khó loại bỏ hoàn toàn nếu không có API trình duyệt mới.
Đã chuyển cho đội kỹ thuật.
Ở chỗ tôi làm, việc mọi người không làm vậy khiến tôi phát điên. Ngay cả giám đốc kỹ thuật cũng thêm vào ticket của mình những việc mất ít thời gian hơn so với cứ xử lý luôn. Dù vậy, việc thường xuyên nghe câu “tôi không tạo ticket để gửi tin nhắn, mà đã nhắn thẳng cho người đó theo cách của bạn” là một tín hiệu tốt.
Ở công ty có rất nhiều lỗi Sentry phát sinh do tiện ích mở rộng trình duyệt làm những chuyện kỳ lạ
Google Translate của Chrome cũng nổi tiếng là làm hỏng các trang dựa trên React
Cuối cùng, việc này biến thành công việc phân loại nhàm chán: lần lượt bỏ qua từng vấn đề do tiện ích mới gây ra. Chúng tôi đang dùng lọc phía client để giảm lượng thu thập. Nhìn chung nhiễu nhiều hơn backend, nên phải đặt ngưỡng cao hơn rất nhiều
Frontend có nhiều lỗi hơn hẳn cũng không có gì đáng ngạc nhiên. Vì nó phải hỗ trợ nhiều biến thể client hơn rất nhiều so với một backend thông thường. Làm một web app lớn hoạt động tốt cho tất cả mọi người có thể cực kỳ khó
Tò mò nếu tiêm một biến có thể làm hỏng web nhiều nhất thì sẽ là gì. Tôi nghĩ đến cái này:
--primary-color: transparent--serif: "Comic Sans MS"Nên đối phó thế nào với các tiện ích mở rộng trình duyệt mang tính đối địch?
Vừa nghĩ vậy tôi mở đại một trang của The Guardian bằng DevTools, thì thấy ai đó đã chèn script và iframe trỏ đến twitter.com
Tôi không thích Grammarly hay mô hình công nghệ của họ, nhưng quy ác ý cho chuyện có thể giải thích đầy đủ bằng sự ngu ngốc thì không công bằng
Tôi đã không làm frontend lâu rồi, nhưng chẳng phải cả tiện ích Grammarly lẫn code của chính mình đều nên dùng tên thuộc tính có namespace tách biệt sao?
Tôi nghĩ có thể lợi dụng chuyện này để hijack plugin đó. Ít nhất có vẻ có thể tiêm văn bản, và có lẽ còn render được một form đăng nhập đẹp mắt để lợi dụng niềm tin của người dùng vào tiện ích
Việc tiêm phần tử vào tài liệu do người khác kiểm soát có thật sự an toàn không?
Việc có thể làm chỉ là bắt chước UI của tiện ích trong chính website, mà cái đó thì đâu cần tiêm gì. Chỉ cần sao chép thiết kế là được