Remove DOM dimension noise and fix document.fonts to prevent site crashes
DOM element dimension noise (offsetWidth/Height etc.) broke complex web apps like Discord by returning fractional values where integers are expected. Removed entirely — measureText noise is sufficient for font enumeration. Changed document.fonts.check() to return true (uniform response) instead of false, which caused font loading logic to hang in apps waiting for fonts.
This commit is contained in:
@@ -27,8 +27,8 @@ Every website you visit is automatically placed in its own isolated container wi
|
|||||||
| Screen | Resolution, color depth, window dimensions |
|
| Screen | Resolution, color depth, window dimensions |
|
||||||
| Timezone | getTimezoneOffset, Date.toString, Intl.DateTimeFormat |
|
| Timezone | getTimezoneOffset, Date.toString, Intl.DateTimeFormat |
|
||||||
| WebRTC | Forced relay-only ICE policy (blocks local IP leak) |
|
| WebRTC | Forced relay-only ICE policy (blocks local IP leak) |
|
||||||
| Fonts | Noise on measureText + DOM element dimensions (offsetWidth/Height etc.) |
|
| Fonts | Noise on measureText (prevents font enumeration) |
|
||||||
| Font API | document.fonts.check() blocked, size reports 0 |
|
| Font API | document.fonts.check() returns uniform response |
|
||||||
| ClientRects | Sub-pixel noise on getBoundingClientRect |
|
| ClientRects | Sub-pixel noise on getBoundingClientRect |
|
||||||
| Plugins | Reports empty |
|
| Plugins | Reports empty |
|
||||||
| Battery | Always reports full/charging |
|
| Battery | Always reports full/charging |
|
||||||
|
|||||||
32
inject.js
32
inject.js
@@ -470,39 +470,13 @@
|
|||||||
return metrics;
|
return metrics;
|
||||||
}, pageWindow.CanvasRenderingContext2D.prototype, { defineAs: "measureText" });
|
}, pageWindow.CanvasRenderingContext2D.prototype, { defineAs: "measureText" });
|
||||||
|
|
||||||
// --- DOM Element Dimension Noise ---
|
|
||||||
// Font enumeration measures offsetWidth/Height of test spans to detect installed fonts.
|
|
||||||
// Adding seeded noise prevents consistent dimension-based font fingerprinting.
|
|
||||||
const fontDimProps = ["offsetWidth", "offsetHeight", "scrollWidth", "scrollHeight", "clientWidth", "clientHeight"];
|
|
||||||
for (const prop of fontDimProps) {
|
|
||||||
const origDesc = Object.getOwnPropertyDescriptor(window.HTMLElement.prototype, prop);
|
|
||||||
if (origDesc && origDesc.get) {
|
|
||||||
const origGet = origDesc.get;
|
|
||||||
Object.defineProperty(pageWindow.HTMLElement.prototype, prop, {
|
|
||||||
get: exportFunction(function() {
|
|
||||||
const val = origGet.call(this);
|
|
||||||
return val + (fontRng() - 0.5) * 0.3;
|
|
||||||
}, pageWindow),
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- document.fonts (FontFaceSet) API Protection ---
|
// --- document.fonts (FontFaceSet) API Protection ---
|
||||||
// document.fonts.check() directly reveals installed fonts; size/iterators expose count.
|
// document.fonts.check() always returns true so font loading logic works,
|
||||||
|
// but prevents enumeration of specific fonts by giving a uniform response.
|
||||||
if (pageWindow.document.fonts) {
|
if (pageWindow.document.fonts) {
|
||||||
try {
|
try {
|
||||||
Object.defineProperty(pageWindow.document.fonts, "check", {
|
Object.defineProperty(pageWindow.document.fonts, "check", {
|
||||||
value: exportFunction(function() { return false; }, pageWindow),
|
value: exportFunction(function() { return true; }, pageWindow),
|
||||||
configurable: true, enumerable: true
|
|
||||||
});
|
|
||||||
Object.defineProperty(pageWindow.document.fonts, "size", {
|
|
||||||
get: exportFunction(function() { return 0; }, pageWindow),
|
|
||||||
configurable: true, enumerable: true
|
|
||||||
});
|
|
||||||
Object.defineProperty(pageWindow.document.fonts, "forEach", {
|
|
||||||
value: exportFunction(function() {}, pageWindow),
|
|
||||||
configurable: true, enumerable: true
|
configurable: true, enumerable: true
|
||||||
});
|
});
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
|
|||||||
@@ -924,12 +924,13 @@ function testDocFonts() {
|
|||||||
report.docfonts = vals;
|
report.docfonts = vals;
|
||||||
|
|
||||||
let html = "";
|
let html = "";
|
||||||
html += row("document.fonts.size", size, size === 0 ? "spoofed" : "real");
|
html += row("document.fonts.size", size);
|
||||||
html += row("check('16px Arial')", checkArial, !checkArial ? "spoofed" : "real");
|
html += row("check('16px Arial')", checkArial, checkArial ? "spoofed" : "real");
|
||||||
html += row("check('16px monospace')", checkMono, !checkMono ? "spoofed" : "real");
|
html += row("check('16px monospace')", checkMono, checkMono ? "spoofed" : "real");
|
||||||
el.innerHTML = html;
|
el.innerHTML = html;
|
||||||
|
|
||||||
const spoofed = size === 0 && !checkArial && !checkMono;
|
// check() returning true for everything = spoofed (uniform response)
|
||||||
|
const spoofed = checkArial && checkMono;
|
||||||
sectionStatus.docfonts = spoofed ? "pass" : "warn";
|
sectionStatus.docfonts = spoofed ? "pass" : "warn";
|
||||||
setDot("dot-docfonts", sectionStatus.docfonts);
|
setDot("dot-docfonts", sectionStatus.docfonts);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user