UNPKG

puppeteer-extra-plugin-stealth

Version:

Stealth mode: Applies various techniques to make detection of headless puppeteer harder.

71 lines (59 loc) 1.92 kB
'use strict' const { PuppeteerExtraPlugin } = require('puppeteer-extra-plugin') const withUtils = require('../_utils/withUtils') /** * Fix `Notification.permission` behaving weirdly in headless mode * * @see https://bugs.chromium.org/p/chromium/issues/detail?id=1052332 */ class Plugin extends PuppeteerExtraPlugin { constructor(opts = {}) { super(opts) } get name() { return 'stealth/evasions/navigator.permissions' } /* global Notification Permissions PermissionStatus */ async onPageCreated(page) { await withUtils(page).evaluateOnNewDocument((utils, opts) => { const isSecure = document.location.protocol.startsWith('https') // In headful on secure origins the permission should be "default", not "denied" if (isSecure) { utils.replaceGetterWithProxy(Notification, 'permission', { apply() { return 'default' } }) } // Another weird behavior: // On insecure origins in headful the state is "denied", // whereas in headless it's "prompt" if (!isSecure) { const handler = { apply(target, ctx, args) { const param = (args || [])[0] const isNotifications = param && param.name && param.name === 'notifications' if (!isNotifications) { return utils.cache.Reflect.apply(...arguments) } return Promise.resolve( Object.setPrototypeOf( { state: 'denied', onchange: null }, PermissionStatus.prototype ) ) } } // Note: Don't use `Object.getPrototypeOf` here utils.replaceWithProxy(Permissions.prototype, 'query', handler) } }, this.opts) } } module.exports = function (pluginConfig) { return new Plugin(pluginConfig) }