fakebrowser
Version:
🤖 Fake fingerprints to bypass anti-bot systems. Simulate mouse and keyboard operations to make behavior like a real person.
79 lines (65 loc) • 2.72 kB
JavaScript
;
const {PuppeteerExtraPlugin} = require('puppeteer-extra-plugin');
const withUtils = require('../_utils/withUtils');
const withWorkerUtils = require('../_utils/withWorkerUtils');
/**
* Mock the `chrome.csi` function if not available (e.g. when running headless).
* It's a deprecated (but unfortunately still existing) chrome specific API to fetch browser timings.
*
* Internally chromium switched the implementation to use the WebPerformance API,
* so we can do the same to create a fully functional mock. :-)
*
* Note: We're using the deprecated PerformanceTiming API instead of the new Navigation Timing Level 2 API on purpopse.
*
* @see https://bugs.chromium.org/p/chromium/issues/detail?id=113048
* @see https://codereview.chromium.org/2456293003/
* @see https://developers.google.com/web/updates/2017/12/chrome-loadtimes-deprecated
* @see https://developer.mozilla.org/en-US/docs/Web/API/PerformanceTiming
* @see https://source.chromium.org/chromium/chromium/src/+/master:chrome/renderer/loadtimes_extension_bindings.cc;l=124?q=loadtimes&ss=chromium
* @see `chrome.loadTimes` evasion
*
*/
class Plugin extends PuppeteerExtraPlugin {
constructor(opts = {}) {
super(opts);
}
get name() {
return 'evasions/chrome.csi';
}
async onPageCreated(page) {
await withUtils(this, page).evaluateOnNewDocument(this.mainFunction);
}
mainFunction = (utils) => {
if (!window.chrome) {
// Use the exact property descriptor found in headful Chrome
// fetch it via `Object.getOwnPropertyDescriptor(window, 'chrome')`
utils.cache.Object.defineProperty(window, 'chrome', {
writable: true,
enumerable: true,
configurable: false, // note!
value: {}, // We'll extend that later
});
}
// That means we're running headful and don't need to mock anything
if ('csi' in window.chrome) {
return; // Nothing to do here
}
// Check that the Navigation Timing API v1 is available, we need that
if (!window.performance || !window.performance.timing) {
return;
}
const {timing} = window.performance;
window.chrome.csi = function () {
return {
onloadT: timing.domContentLoadedEventEnd,
startE: timing.navigationStart,
pageT: Date.now() - timing.navigationStart,
tran: 15, // Transition type or something
};
};
utils.patchToString(window.chrome.csi);
};
}
module.exports = function (pluginConfig) {
return new Plugin(pluginConfig);
};