Học từ Facebook #51: Kiểm tra hàm native của trang có bị bọc không
Đoạn code ẩn trong ClickRefLogger của Facebook bọc hàm native để dò script lạ. Bài phân tích cách nó làm và cách một trang tự kiểm tra API native mình dùng.
Trang thanh toán của bạn gọi fetch('/api/pay', …). Bạn có chắc fetch đó là fetch của trình duyệt không? Bất kỳ script nào chạy trên cùng trang (thư viện quảng cáo, chat widget, extension của người dùng, mã độc được chèn vào qua một dependency) đều có thể gán window.fetch = function () { … } và đọc mọi request trước khi chuyển tiếp.
Facebook có một đoạn code làm đúng việc bọc hàm native như thế, nhưng theo hướng ngược lại: họ bọc hàm để phát hiện script lạ đang chạy trên trang. Bài này phân tích đoạn code đó, chỉ ra vì sao cách "giả toString" của nó vẫn để lại dấu vết, và viết một bộ kiểm tra giúp trang của bạn biết API native mình dựa vào có còn nguyên vẹn không.
Tip
Tóm tắt
ClickRefLoggercủa Facebook, ngoài việc log click, còn chạy một bộ luật do server cấp. Chuỗi luật được che bằng XOR 1. Luật có thể bọc một hàm native, ghi lại tham số khớp regex, rồi gửi kết quả lên server.- Hàm bọc gán
wrapper.toString = original.toString.bind(original), nênfetch.toString()vẫn in[native code]. - Cách này vẫn lộ: in hàm bằng
Function.prototype.toStringlấy từ một iframe sạch, kiểm tra thuộc tínhtoStringriêng,name/length, và descriptor của thuộc tính. - Quan trọng hơn kiểm tra: chụp tham chiếu sớm (
const nativeFetch = window.fetchtrước mọi script khác) và chỉ dùng bản đã chụp cho thao tác nhạy cảm. Mọi kiểm tra kiểu này đều chỉ là tín hiệu, không phải ranh giới bảo mật.
▶ Mở demo toàn màn hình ↗ · Sửa code trên StackBlitz ↗ · Xem source trên GitHub ↗
#Facebook làm thế nào?
#Một logger click bình thường…
ClickRefLogger (283.js) nghe sự kiện ClickRefAction/new của Arbiter, gom thông tin click (vị trí, phím bổ trợ mã hoá thành bitmask ctrl=1, shift=2, alt=4, meta=8, data-ft, URL hiện tại…) rồi gửi lên Banzai với route click_ref_logger. Phần này giống các logger khác trong bundle.
#…và một hàm chạy ẩn ở cuối module
Cuối module có một hàm được gọi ngay khi module tải, bọc trong try {} catch {} để không bao giờ ném lỗi ra ngoài:
function c(Env) {
const xor1 = (s) => [...s].map((ch) => String.fromCharCode(1 ^ ch.charCodeAt(0))).join('');
const rules = Env[xor1('jiri')]; // 'jiri' → 'khsh'
if (!rules) return;
xor1(rules).split(',').map((rule, index) => { /* switch (rule.charAt(0)) … */ });
}
try { c(Env); } catch (e) {}
js
Mỗi luật bắt đầu bằng một chữ số chỉ loại: 1 đánh dấu khi window[tên] tồn tại; 2 bọc hàm tại một path trên window và test tham số bằng regex; 3 bọc một hàm "kích hoạt", khi nó chạy thì kiểm các luật loại 1 rồi gửi kết quả; 4 là cờ. (Vòng lặp xor1 ở đây viết gọn lại; bản gốc dùng for và fromCharCode.)
Vài điểm đáng chú ý:
- Tên khoá cấu hình, tên hàm gửi và tên route đều được che bằng XOR 1:
xor1('jiri') = 'khsh',xor1('qnru') = 'post',xor1('islg') = 'hrmf'. Kết quả gửi đi làBanzai.post('hrmf', { m: danhSáchLuậtKhớp }), sau 5 giây kể từ khi hàm kích hoạt chạy. - Giá trị
Env.khshkhông có trong bundle. Nó được server cấp lúc render trang, nên không thể biết luật cụ thể là gì, cũng không biết luật có đang được bật cho ai không. Mẫu này giống cách dò extension, công cụ tự động hoá hoặc script chèn vào trang, nhưng đó là suy đoán dựa trên cấu trúc code. - Mỗi luật chỉ được báo một lần cho mỗi lần tải trang (
if (reported[index]) return).
#Cách bọc hàm
function wrap(obj, path, i, onCall) {
const key = path[i];
if (!(key && obj && key in obj)) return;
if (i + 1 < path.length) return wrap(obj[key], path, i + 1, onCall);
const original = obj[key];
const wrapper = function () {
window.setTimeout(onCall.bind(null, arguments)); // ghi nhận sau, không làm chậm hàm gốc
return original.apply(this, arguments);
};
wrapper.toString = original.toString.bind(original); // in ra vẫn là [native code]
Object.defineProperty(obj, key, { configurable: false, writable: true, value: wrapper });
}
js
Ba lựa chọn có chủ đích:
- Ghi nhận bất đồng bộ.
setTimeoutđẩy việc test regex ra khỏi lời gọi, để hàm gốc không chậm đi. - Giả
toString. Ai kiểm tra bằngfn.toString().includes('[native code]')sẽ thấy hàm vẫn "native". configurable: false. Sau khi bọc, không script nàodefinePropertylại được thuộc tính đó. Gán thường vẫn được vìwritable: true.
Hai tác dụng phụ đáng biết: hàm bọc dùng apply(this, arguments) nên không gọi được bằng new. Bọc một constructor như WebSocket thì new WebSocket(…) sẽ ném TypeError. Và vì configurable: false, polyfill hay thư viện nào chạy sau mà cần defineProperty lại thuộc tính đó sẽ lỗi.
#Bên trong trình duyệt: vì sao giả toString vẫn lộ
flowchart TD
F["window.fetch (đã bị bọc)"] --> A["fetch.toString()"]
A -->|"gọi toString riêng của wrapper"| N1["'function fetch() { [native code] }'"]
F --> B["cleanToString.call(fetch)"]
B -->|"Function.prototype.toString của iframe sạch"| N2["in mã nguồn thật của wrapper"]
F --> C["Object.hasOwn(fetch, 'toString')"]
C --> N3["true: hàm native không có toString riêng"]
F --> D["fetch.name, fetch.length"]
D --> N4["'wrapper' / 0 khác 'fetch' / 1"]
fn.toString()tra thuộc tínhtoStringtrên chínhfntrước. Wrapper có thuộc tính riêng, nên câu trả lời là của hàm gốc.Function.prototype.toString.call(fn)thì đi thẳng vào hàm dựng sẵn của engine, bỏ qua thuộc tính riêng. Nhưng chínhFunction.prototype.toStringcủa trang cũng có thể đã bị thay. Lấy nó từ một iframeabout:blankmới tạo cho ta một bản mà script của trang hiện tại chưa đụng tới. Gọi chéo realm vẫn hợp lệ với iframe cùng origin.- Hàm native có
namevàlengthcố định (fetchcó length 1). Wrapper viết bằngfunction () {}có length 0 và name khác. - Descriptor trên
windowcủafetchgốc làconfigurable: true. Sau khi bọc kiểu trên, nó thànhfalse.
Không kiểm tra nào trong số này là tuyệt đối. Script chạy trước bạn có thể sửa luôn cách tạo iframe. Extension có quyền chèn script vào mọi frame thì chạm được cả iframe "sạch". Mục tiêu chỉ là nâng chi phí và có tín hiệu để ghi log.
#Viết lại
#Mở realm sạch và kiểm tra một hàm
openCleanRealm tạo một iframe ẩn about:blank và trả về contentWindow của nó (xem bản đầy đủ trong spoiler bên dưới). Phần kiểm tra:
src/lib/nativeHookCheck.ts
export function checkNative(path: string, pageRoot: unknown, realm: ReturnType<typeof openCleanRealm>): IntegrityResult {
const page = resolvePath(pageRoot, path);
const clean = resolvePath(realm.win, path);
const checks: IntegrityResult['checks'] = [];
const fn = page.value;
if (typeof fn !== 'function') {
return { path, ok: false, checks: [{ name: 'là function', pass: false, detail: typeof fn }] };
}
let printed = '';
try {
printed = realm.toString.call(fn);
} catch (error) {
printed = `lỗi: ${(error as Error).message}`;
}
checks.push({ name: 'toString của realm sạch ra [native code]', pass: NATIVE_RE.test(printed), detail: printed.slice(0, 60) });
const ownToString = Object.prototype.hasOwnProperty.call(fn, 'toString');
checks.push({ name: 'không có toString riêng', pass: !ownToString });
const cleanFn = clean.value as ((...args: unknown[]) => unknown) | undefined;
if (typeof cleanFn === 'function') {
checks.push({
name: 'name và length khớp bản sạch',
pass: fn.name === cleanFn.name && fn.length === cleanFn.length,
detail: `${fn.name}/${fn.length} vs ${cleanFn.name}/${cleanFn.length}`,
});
}
if (page.holder && clean.holder) {
const a = Object.getOwnPropertyDescriptor(page.holder, page.key);
const b = Object.getOwnPropertyDescriptor(clean.holder, clean.key);
if (a && b) {
const same = a.configurable === b.configurable && a.enumerable === b.enumerable && ('value' in a) === ('value' in b);
checks.push({
name: 'descriptor khớp bản sạch',
pass: same,
detail: `configurable ${a.configurable}/${b.configurable}`,
});
}
}
return { path, ok: checks.every((c) => c.pass), checks };
}
ts
#Chụp tham chiếu sớm
src/lib/nativeHookCheck.ts
/**
* Cách phòng thủ thực dụng nhất: chụp tham chiếu NGAY khi script của bạn chạy (càng sớm
* càng tốt, ví dụ inline trong <head>) rồi chỉ dùng bản đã chụp cho thao tác nhạy cảm.
*/
export function snapshotNatives<T extends Record<string, string>>(paths: T, root: unknown = window) {
const out = {} as { [K in keyof T]: (...args: never[]) => unknown };
for (const [name, path] of Object.entries(paths)) {
const { holder, value } = resolvePath(root, path);
if (typeof value === 'function') {
// giữ `this` gốc cho hàm toàn cục như fetch; hàm trên prototype gọi bằng .call(target)
out[name as keyof T] = (holder === root ? value.bind(root) : value) as (...args: never[]) => unknown;
}
}
return out;
}
ts
Xem toàn bộ src/lib/nativeHookCheck.ts
src/lib/nativeHookCheck.ts
/**
* Kiểm tra tính toàn vẹn của các API native mà trang của mình phụ thuộc vào
* (bối cảnh: ClickRefLogger của Facebook hook hàm native để dò script lạ).
*
* Mục đích ở đây là PHÒNG THỦ: trang thanh toán, trang đăng nhập muốn biết `fetch`,
* `XMLHttpRequest.prototype.open`, `addEventListener`… có đang bị một script bên thứ ba
* (extension, thư viện quảng cáo, mã độc chèn vào) bọc lại hay không, để quyết định
* có nên dùng bản tham chiếu đã chụp sẵn, hay ghi log cảnh báo.
*
* Đây KHÔNG phải ranh giới bảo mật: script chạy trước bạn trên cùng trang có thể sửa
* luôn những công cụ kiểm tra. Kết quả chỉ là tín hiệu.
*/
export interface IntegrityResult {
path: string;
ok: boolean;
checks: Array<{ name: string; pass: boolean; detail?: string }>;
}
/**
* Mở một realm sạch (iframe about:blank, không gắn src) để lấy Function.prototype.toString
* và các hàm tham chiếu chưa bị script của trang hiện tại đụng tới.
* Giới hạn: extension có content script `all_frames`/`match_about_blank` vẫn có thể chạm vào.
*/
export function openCleanRealm(doc: Document = document) {
const iframe = doc.createElement('iframe');
iframe.style.display = 'none';
iframe.setAttribute('aria-hidden', 'true');
doc.documentElement.appendChild(iframe);
const win = iframe.contentWindow as (Window & typeof globalThis) | null;
if (!win) {
iframe.remove();
throw new Error('Không mở được realm sạch');
}
return {
win,
toString: win.Function.prototype.toString as (this: unknown) => string,
dispose: () => iframe.remove(),
};
}
export function resolvePath(root: unknown, path: string): { holder: object | null; key: string; value: unknown } {
const parts = path.split('.');
let holder: unknown = root;
for (const part of parts.slice(0, -1)) {
holder = holder == null ? undefined : (holder as Record<string, unknown>)[part];
}
const key = parts[parts.length - 1];
const value = holder == null ? undefined : (holder as Record<string, unknown>)[key];
return { holder: (holder as object) ?? null, key, value };
}
const NATIVE_RE = /^function [\w$]+\(\) \{\s*\[native code\]\s*\}$/;
/**
* So một hàm của trang với bản cùng đường dẫn trong realm sạch:
* 1. dùng toString CỦA REALM SẠCH để in hàm (hook giả `fn.toString` không ảnh hưởng)
* 2. hàm không được có thuộc tính `toString` riêng
* 3. `name` và `length` khớp với bản sạch
* 4. descriptor trên đối tượng chứa (configurable/writable/enumerable) khớp với bản sạch
*/
export function checkNative(path: string, pageRoot: unknown, realm: ReturnType<typeof openCleanRealm>): IntegrityResult {
const page = resolvePath(pageRoot, path);
const clean = resolvePath(realm.win, path);
const checks: IntegrityResult['checks'] = [];
const fn = page.value;
if (typeof fn !== 'function') {
return { path, ok: false, checks: [{ name: 'là function', pass: false, detail: typeof fn }] };
}
let printed = '';
try {
printed = realm.toString.call(fn);
} catch (error) {
printed = `lỗi: ${(error as Error).message}`;
}
checks.push({ name: 'toString của realm sạch ra [native code]', pass: NATIVE_RE.test(printed), detail: printed.slice(0, 60) });
const ownToString = Object.prototype.hasOwnProperty.call(fn, 'toString');
checks.push({ name: 'không có toString riêng', pass: !ownToString });
const cleanFn = clean.value as ((...args: unknown[]) => unknown) | undefined;
if (typeof cleanFn === 'function') {
checks.push({
name: 'name và length khớp bản sạch',
pass: fn.name === cleanFn.name && fn.length === cleanFn.length,
detail: `${fn.name}/${fn.length} vs ${cleanFn.name}/${cleanFn.length}`,
});
}
if (page.holder && clean.holder) {
const a = Object.getOwnPropertyDescriptor(page.holder, page.key);
const b = Object.getOwnPropertyDescriptor(clean.holder, clean.key);
if (a && b) {
const same = a.configurable === b.configurable && a.enumerable === b.enumerable && ('value' in a) === ('value' in b);
checks.push({
name: 'descriptor khớp bản sạch',
pass: same,
detail: `configurable ${a.configurable}/${b.configurable}`,
});
}
}
return { path, ok: checks.every((c) => c.pass), checks };
}
export function checkAll(paths: string[], pageRoot: unknown = window): IntegrityResult[] {
const realm = openCleanRealm();
try {
return paths.map((p) => checkNative(p, pageRoot, realm));
} finally {
realm.dispose();
}
}
/**
* Cách phòng thủ thực dụng nhất: chụp tham chiếu NGAY khi script của bạn chạy (càng sớm
* càng tốt, ví dụ inline trong <head>) rồi chỉ dùng bản đã chụp cho thao tác nhạy cảm.
*/
export function snapshotNatives<T extends Record<string, string>>(paths: T, root: unknown = window) {
const out = {} as { [K in keyof T]: (...args: never[]) => unknown };
for (const [name, path] of Object.entries(paths)) {
const { holder, value } = resolvePath(root, path);
if (typeof value === 'function') {
// giữ `this` gốc cho hàm toàn cục như fetch; hàm trên prototype gọi bằng .call(target)
out[name as keyof T] = (holder === root ? value.bind(root) : value) as (...args: never[]) => unknown;
}
}
return out;
}
/** Giải mã chuỗi kiểu ClickRefLogger: mỗi ký tự XOR 1 ("jiri" → "khsh"). Chỉ là che mắt, không phải mã hoá. */
export const xor1 = (text: string) => Array.from(text, (ch) => String.fromCharCode(ch.charCodeAt(0) ^ 1)).join('');
ts
#Đi qua code
openCleanRealmtạo iframe không cósrc. Trình duyệt tạo sẵn một documentabout:blankđồng bộ, nêncontentWindowdùng được ngay mà không phải chờload.checkNativein hàm bằngrealm.toString, không bằngfn.toString(). Regex chấp nhận cả cách in của Chrome ({ [native code] }trên một dòng) lẫn Firefox (xuống dòng).- Kiểm
name/lengthbằng cách so với cùng đường dẫn trong realm sạch, không cần bảng hằng số viết tay. checkAlltạo và huỷ realm cho mỗi lần kiểm, để không giữ iframe thừa trong DOM.snapshotNativeslà phần nên dùng nhiều nhất: đặt nó trong script chạy đầu tiên của trang, rồi gọisnap.fetch(…)thay chofetch(…)ở các luồng nhạy cảm như thanh toán, đổi mật khẩu.
Khác bản gốc: bản gốc là bộ dò do server điều khiển, gửi kết quả về server. Bản viết lại chỉ kiểm tra API mà chính trang dùng, trả kết quả cho code của trang, không gửi đi đâu.
#Tại sao không làm cách khác?
So fn === window.fetch lúc khởi động? Đó chính là chụp tham chiếu. Nó tốt nhất nếu script của bạn chạy trước mọi script khác. Kiểm tra realm sạch hữu ích khi bạn không kiểm soát được thứ tự, ví dụ code của bạn là một SDK được nhúng vào trang người khác.
Đóng băng bằng Object.freeze(window.fetch)? Không ngăn được việc gán window.fetch = …, vì freeze áp lên hàm chứ không phải thuộc tính trên window. Dùng Object.defineProperty(window, 'fetch', { writable: false, configurable: false }) thì chặn được gán sau đó, nhưng cũng chặn luôn polyfill và công cụ giám sát hợp lệ (Sentry, OpenTelemetry cũng bọc fetch).
Content Security Policy? CSP với script-src chặt chẽ ngăn script lạ được tải ngay từ đầu, và đây mới là lớp bảo vệ chính. Kiểm tra integrity chỉ là lớp phát hiện cho những gì lọt qua (extension không bị CSP của trang chặn).
#Edge case và cạm bẫy
- Công cụ hợp lệ cũng bọc hàm. Sentry, Datadog RUM, OpenTelemetry, công cụ đo hiệu năng đều bọc
fetch/XMLHttpRequest. Kiểm tra sẽ báo "bị bọc" và điều đó đúng. Đừng chặn người dùng chỉ vì kết quả này; hãy ghi log, hoặc dùng bản đã chụp. - Hàm bound.
fetch.bind(window)in rafunction () { [native code] }, không có tên trong chuỗi (dù.namelà"bound fetch"). Regex kiểm tra yêu cầu tên, nên đừng kiểm tra chính bản snapshot bằngcheckNative. - Proxy.
new Proxy(fetch, {})giữ nguyênnamevàlength, vàFunction.prototype.toStringvẫn in[native code], nhưng trên V8 chuỗi in ra không có tên hàm:function () { [native code] }. Regex trong bài bắt điểm này (đã thử trên Chromium và Node). Đây là chi tiết của engine, không có trong đặc tả, nên đừng dựa hẳn vào nó. - Không tạo được iframe. Trang dùng sandbox chặt hoặc chính sách tuỳ biến có thể chặn thao tác DOM.
checkAllđể lỗi ném ra; hãy bọc trongtryvà coi là "không kiểm được". - Gọi trong lúc trang đang tải.
document.documentElementphải tồn tại. Script inline trong<head>thì đã có.
#Quyền riêng tư
Dò xem người dùng có extension nào, có biến toàn cục nào, là một dạng lấy dấu vân tay trình duyệt (fingerprinting). Ở EU, đọc thông tin từ thiết bị người dùng mà không thật sự cần cho dịch vụ thường cần sự đồng ý (ePrivacy). Cách làm của bản gốc (luật do server cấp, tên che bằng XOR, không có giao diện nào cho người dùng biết) khó kiểm toán từ bên ngoài, ngay cả khi mục đích là chống gian lận hay chống bot.
Kiểm tra trong bài có phạm vi hẹp hơn: chỉ xem API mà chính trang dùng có còn nguyên không, và chỉ dùng kết quả ở client. Nếu cần gửi về server để phân tích, hãy ghi rõ trong chính sách quyền riêng tư và chỉ gửi kết quả tổng hợp ("fetch bị bọc: có/không"), không gửi danh sách extension hay biến toàn cục.
#Dùng trong dự án
<head>
<script>
// chạy trước mọi script khác
window.__native = { fetch: window.fetch.bind(window) };
</script>
…
</head>
html
import { checkAll } from './nativeHookCheck';
export async function pay(body: PaymentRequest) {
const report = checkAll(['fetch', 'XMLHttpRequest.prototype.open']);
if (report.some((r) => !r.ok)) logIntegrityWarning(report.map((r) => [r.path, r.ok]));
// luôn dùng bản đã chụp cho request nhạy cảm, bất kể kết quả kiểm tra
return window.__native.fetch('/api/pay', { method: 'POST', body: JSON.stringify(body) });
}
ts
#Kiểm thử
Ví dụ test (bản phác, không có trong repo)
import { describe, expect, it } from 'vitest';
import { checkAll, xor1 } from './nativeHookCheck';
describe('checkAll', () => {
it('hàm native không bị bọc thì sạch', () => {
expect(checkAll(['JSON.stringify'])[0].ok).toBe(true);
});
it('hook giả toString vẫn bị phát hiện', () => {
const original = JSON.stringify;
const wrapper = function (this: unknown) {
return original.apply(this, arguments as unknown as Parameters<typeof JSON.stringify>);
};
wrapper.toString = original.toString.bind(original);
JSON.stringify = wrapper as typeof JSON.stringify;
try {
expect(String(JSON.stringify)).toContain('[native code]'); // cách hỏi ngây thơ bị lừa
const [result] = checkAll(['JSON.stringify']);
expect(result.ok).toBe(false);
} finally {
JSON.stringify = original;
}
});
});
it('xor1 giải chuỗi bị che', () => {
expect(['jiri', 'qnru', 'islg'].map(xor1)).toEqual(['khsh', 'post', 'hrmf']);
});
ts
#So với chuẩn web
- CSP (
script-srcvới nonce/hash) và Subresource Integrity (integrity="sha384-…"trên thẻ<script>) ngăn script lạ hoặc script bị sửa được chạy. Đây là lớp nên có trước. - Trusted Types chặn việc đưa chuỗi vào các sink nguy hiểm như
innerHTML, giảm khả năng mã độc được chèn qua XSS. - Trình duyệt chưa có API chuẩn nào để hỏi "hàm này có phải native và chưa bị sửa không" (tính tới 10/2026). Các kỹ thuật trong bài đều dựa trên hành vi hiện có của
Function.prototype.toStringvà iframe cùng origin.
Bài này thuộc series Học từ mã nguồn Facebook (106 bài, đọc theo thứ tự).
Xem nhanh danh sách bài trong series
Phần 1
- Mở đầu: cách đọc code minify của Facebook
- #1 Dùng chung IntersectionObserver + infinite scroll
- #2 useViewportDuration: đo thời gian thực sự được xem
- #3 Preload code + data khi hover
- #4 Toast manager với external store
- #5 Network status + heartbeat backoff
- #6 ErrorGuard: bắt lỗi theo ngữ cảnh
- #7 Banzai: gom log theo lô + sendBeacon
- #8 Focus trap + Esc theo layer
Phần 2
- #9 Mẹo calc(… * 9999): container query, line-clamp, mũi tên carousel bằng CSS
- #10 Thanh cuộn tuỳ biến bằng CSS 3D
- #11 Modal cuộn bằng window, giữ vị trí trang nền
- #12 Popover, tooltip group và hovercard
- #13 Primitive Pressable + focus-visible
- #14 Tab "Xem thêm" + sticky header bằng IntersectionObserver
- #15 Avatar khoét mask, gradient Messenger, skeleton steps()
- #16 Khung chat không giật
- #17 Thuật toán chọn bố cục collage ảnh
- #18 Keep-alive tab trong SPA
- #19 Tự đo Visual Completion & interaction tracing
- #20 Router SPA bền bỉ & tải chunk có retry
Phần 3
- #21 Smart Compose: gợi ý chữ mờ khi gõ
- #22 MQTT ngay trong trình duyệt, tự chuyển sang long-poll
- #23 Đo thời gian dùng bằng bitmap 64 giây
- #24 Bảng reaction nhấn giữ rồi trượt
- #25 Tìm kiếm gấp dấu, phiên âm và highlight đầu từ
- #26 Ring buffer biết trace nào đã mất log
- #27 Engine animation JS: một vòng rAF và solver cubic-bezier
- #28 Bootloader: bitmap tài nguyên đã tải, chỉ reload khi cần
- #29 Nhận biết click do screen reader tạo ra
- #30 Tải trước feed theo chất lượng mạng, đoán hạng thiết bị
- #31 Hàng đợi gửi tin chat giữ đúng thứ tự
- #32 Gộp lời gọi API trong cùng một tick
- #33 Resize ảnh trong pool Web Worker
- #34 Crop ảnh theo nhiều khuôn mặt
- #35 Kéo-thả sắp xếp và vùng thả file không nhấp nháy
- #36 Tiện ích UI: fade-out, sticky event, hàm huỷ được
- #37 Hạ tầng nhỏ: cookie, hash lỗi, prefetch khi chuột dừng
Phần 4–6
- #38 Đồng bộ tin nhắn: phát hiện lỗ seqID và fetch bù theo tầng
- #39 OTID: ID tin nhắn kiểu Snowflake sinh ở client
- #40 QPL: lấy mẫu từ lúc bắt đầu, đo RTT gửi tin đầu-cuối
- #41 Bộ điều phối request bảy chế độ gộp và chống trùng
- #42 Thread list theo mốc thời gian, cookie presence có version
- #43 Composer dạng plugin: reducer, dirty checker, chuỗi kiểm tra
- #44 Thanh toán bất đồng bộ: subscribe trước, poll dự phòng
- #45 Validate số thẻ: sửa lỗi gõ, dải BIN, Luhn
- #46 Lịch chọn khoảng ngày: bo góc dải chọn, phím đủ bộ, RTL
- #47 Giờ mở cửa theo tuần: qua đêm, gộp khoảng, phần bù
- #48 Tiến độ đăng realtime: giữ event chờ snapshot, watchdog
- #49 Tiền tệ theo đơn vị nhỏ nhất và ô nhập tiền
- #50 Sửa hàng loạt Empty/Uniform/Mixed, seen/read bằng 2 bit
- #51 Phát hiện script lạ hook vào hàm native (bài này)
- #52 localStorage tự dọn khi đầy quota
- #53 Cache tuyến do server phân giải, chờ sẵn promise
- #54 Lưới phím mũi tên 2D và focus không làm cuộn trang
- #55 Tách emoji bằng máy trạng thái
- #56 Đọc trước ghi sau trong một frame, con tự biết vị trí
- #57 Hàng đợi lệnh cho video và stream sắp lại chunk
Phần 7–10
- #58 Ước lượng băng thông EWMA: loại outlier, nhớ qua phiên
- #59 ABR theo độ tự tin phát và cắt bậc bitrate theo MOS
- #60 Prefetch segment dự đoán từ manifest, parse hộp sidx
- #61 Đọc metadata MP4/MOV ngay trên client
- #62 Upload tiếp tục được: offset, hash dedupe, phát hiện treo
- #63 Fenwick tree: chọn ngẫu nhiên có trọng số và offset dòng
- #64 Vạch trục số và trục thời gian "đẹp"
- #65 Damerau-Levenshtein theo dải chéo và tự sửa tên gõ sai
- #66 PRNG có seed, shuffle tái lập và lấy mẫu trace
- #67 Cặp min/max-heap tái dùng vị trí trong danh sách ảo
- #68 Memo nhiều đối số bằng Map/WeakMap và interning object
- #69 Thống kê cửa sổ trượt và đại số khoảng thời gian
- #70 Int64 từ hai số 32-bit, decode UTF, offset theo code point
- #71 Engine múi giờ IANA trên client, phát hiện lệch giờ
- #72 Tự cài semver range
- #73 Auto-link URL, cắt chữ an toàn, giới hạn LaTeX
- #74 Parser query kiểu PHP chống prototype pollution
- #75 Popover tự lật theo phần tràn nhỏ nhất
- #76 Tiền tố for(;😉 chống JSON hijacking, đọc stream
- #77 Chống lộ Referer, linkshim, xoá token trên URL
- #78 setImmediate bằng postMessage, gom delta chuột theo frame
- #79 Phát hiện crash qua heartbeat, dò proxy cache hỏng
- #80 DPR vào cookie cho ảnh @2x, che khoá trong log
- #81 Flux waitFor tự viết và proxy có đệm
- #82 Crop đúng tỉ lệ, xoay sticker bằng ma trận
Phần 11–15
- #83 Chia subdomain giữa các tab bằng lease, long-poll theo bảng trạng thái
- #84 MQTT QoS1: giữ publish chờ ack, gửi lại khi kết nối lại
- #85 Cache Promise gộp request, cache lỗi ngắn, đua dữ liệu nhúng sẵn
- #86 Lịch retry: backoff có trần, Fibonacci, báo lỗi CDN mỗi N ngày
- #87 Giữ nguyên tham chiếu nhánh dữ liệu không đổi
- #88 Store optimistic: copy-on-write, rollback rồi áp lại
- #89 Chỉ mục subscription theo record, hàng đợi task theo chiều sâu
- #90 Phân trang connection: cursor khớp đầu, edge không trùng
- #91 Profiler kiểu middleware và memo selector có lấy mẫu kiểm tra
- #92 Đọc EBML/WebM Cues để tìm byte range của segment
- #93 Stream HTTP Range tạm dừng được, tải tiếp đúng byte
- #94 Bảng khoảng thời gian gắn nhãn và nhảy qua lỗ buffer
- #95 Gom chunk cho SourceBuffer, đồng bộ video bằng playbackRate
- #96 Gom tin nhắn thành cụm, vạch ngày và avatar đã xem
- #97 Bao nhiêu tab chat vừa màn hình, chuỗi luật mở tab
- #98 Phím tắt Markdown trong editor, Backspace để hoàn tác
- #99 Offset theo code point hay UTF-16: bốn bug cùng một gốc
- #100 Typeahead không nhảy: cache tiền tố, chặn reflow, khoá hover
- #101 Hit-test trên canvas bằng phát lại path, nét đứt và cung Bézier
- #102 Vòng tròn theo mét trên bản đồ Mercator
- #103 Dò tính năng trình duyệt: ảnh EXIF 2×1, getter bẫy, scrollbar
- #104 Bug định dạng số và kiểm tra dữ liệu theo locale
- #105 Dispose cho đúng: idle callback, splice(-1), xoá nhầm key
Code + demo: examples/facebook-patterns · Mở trên StackBlitz

