Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

puppeteer-extra-plugin-stealth

Package Overview
Dependencies
Maintainers
1
Versions
69
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

puppeteer-extra-plugin-stealth - npm Package Compare versions

Comparing version 2.1.1 to 2.1.2

evasions/webgl.vendor/index.js

2

evasions/_template/readme.md

@@ -9,3 +9,3 @@ ## API

### [Plugin](https://github.com/berstend/puppeteer-extra/blob/db57ea66cf10d407cf63af387892492e495a84f2/packages/puppeteer-extra-plugin-stealth/evasions/_template/index.js#L10-L20)
### [Plugin](https://git@github.com/:berstend/puppeteer-extra/blob/ff112879545e8e68d6500d731ceeafc22d187dd3/packages/puppeteer-extra-plugin-stealth/evasions/_template/index.js#L10-L20)

@@ -12,0 +12,0 @@ **Extends: PuppeteerExtraPlugin**

@@ -9,3 +9,3 @@ ## API

### [Plugin](https://github.com/berstend/puppeteer-extra/blob/db57ea66cf10d407cf63af387892492e495a84f2/packages/puppeteer-extra-plugin-stealth/evasions/chrome.runtime/index.js#L10-L22)
### [Plugin](https://git@github.com/:berstend/puppeteer-extra/blob/ff112879545e8e68d6500d731ceeafc22d187dd3/packages/puppeteer-extra-plugin-stealth/evasions/chrome.runtime/index.js#L10-L22)

@@ -12,0 +12,0 @@ **Extends: PuppeteerExtraPlugin**

@@ -9,3 +9,3 @@ ## API

### [Plugin](https://github.com/berstend/puppeteer-extra/blob/db57ea66cf10d407cf63af387892492e495a84f2/packages/puppeteer-extra-plugin-stealth/evasions/console.debug/index.js#L8-L18)
### [Plugin](https://git@github.com/:berstend/puppeteer-extra/blob/ff112879545e8e68d6500d731ceeafc22d187dd3/packages/puppeteer-extra-plugin-stealth/evasions/console.debug/index.js#L8-L18)

@@ -12,0 +12,0 @@ **Extends: PuppeteerExtraPlugin**

@@ -9,3 +9,3 @@ ## API

### [Plugin](https://github.com/berstend/puppeteer-extra/blob/db57ea66cf10d407cf63af387892492e495a84f2/packages/puppeteer-extra-plugin-stealth/evasions/iframe.contentWindow/index.js#L8-L23)
### [Plugin](https://git@github.com/:berstend/puppeteer-extra/blob/ff112879545e8e68d6500d731ceeafc22d187dd3/packages/puppeteer-extra-plugin-stealth/evasions/iframe.contentWindow/index.js#L8-L23)

@@ -12,0 +12,0 @@ **Extends: PuppeteerExtraPlugin**

@@ -9,3 +9,3 @@ ## API

### [Plugin](https://github.com/berstend/puppeteer-extra/blob/db57ea66cf10d407cf63af387892492e495a84f2/packages/puppeteer-extra-plugin-stealth/evasions/navigator.languages/index.js#L8-L21)
### [Plugin](https://git@github.com/:berstend/puppeteer-extra/blob/ff112879545e8e68d6500d731ceeafc22d187dd3/packages/puppeteer-extra-plugin-stealth/evasions/navigator.languages/index.js#L8-L21)

@@ -12,0 +12,0 @@ **Extends: PuppeteerExtraPlugin**

@@ -9,3 +9,3 @@ ## API

### [Plugin](https://github.com/berstend/puppeteer-extra/blob/db57ea66cf10d407cf63af387892492e495a84f2/packages/puppeteer-extra-plugin-stealth/evasions/navigator.permissions/index.js#L8-L46)
### [Plugin](https://git@github.com/:berstend/puppeteer-extra/blob/ff112879545e8e68d6500d731ceeafc22d187dd3/packages/puppeteer-extra-plugin-stealth/evasions/navigator.permissions/index.js#L8-L46)

@@ -12,0 +12,0 @@ **Extends: PuppeteerExtraPlugin**

@@ -6,17 +6,210 @@ 'use strict'

/**
* Pass the Plugins Length Test.
* In headless mode `navigator.mimeTypes` and `navigator.plugins` are empty.
* This plugin quite emulates both of these to match regular headful Chrome.
* We even go so far as to mock functional methods, instance types and `.toString` properties. :D
*/
class Plugin extends PuppeteerExtraPlugin {
constructor (opts = { }) { super(opts) }
constructor (opts = {}) {
super(opts)
}
get name () { return 'stealth/evasions/navigator.plugins' }
get name () {
return 'stealth/evasions/navigator.plugins'
}
async onPageCreated (page) {
await page.evaluateOnNewDocument(() => {
// Overwrite the `plugins` property to use a custom getter.
Object.defineProperty(navigator, 'plugins', {
// This just needs to have `length > 0` for the current test,
// but we could mock the plugins too if necessary.
get: () => [1, 2, 3, 4, 5]
})
function mockPluginsAndMimeTypes () {
/* global MimeType MimeTypeArray PluginArray */
// Disguise custom functions as being native
const makeFnsNative = (fns = []) => {
const oldCall = Function.prototype.call
function call () {
return oldCall.apply(this, arguments)
}
// eslint-disable-next-line
Function.prototype.call = call
const nativeToStringFunctionString = Error.toString().replace(
/Error/g,
'toString'
)
const oldToString = Function.prototype.toString
function functionToString () {
for (const fn of fns) {
if (this === fn.ref) {
return `function ${fn.name}() { [native code] }`
}
}
if (this === functionToString) {
return nativeToStringFunctionString
}
return oldCall.call(oldToString, this)
}
// eslint-disable-next-line
Function.prototype.toString = functionToString
}
const mockedFns = []
const fakeData = {
mimeTypes: [
{
type: 'application/pdf',
suffixes: 'pdf',
description: '',
__pluginName: 'Chrome PDF Viewer'
},
{
type: 'application/x-google-chrome-pdf',
suffixes: 'pdf',
description: 'Portable Document Format',
__pluginName: 'Chrome PDF Plugin'
},
{
type: 'application/x-nacl',
suffixes: '',
description: 'Native Client Executable',
enabledPlugin: Plugin,
__pluginName: 'Native Client'
},
{
type: 'application/x-pnacl',
suffixes: '',
description: 'Portable Native Client Executable',
__pluginName: 'Native Client'
}
],
plugins: [
{
name: 'Chrome PDF Plugin',
filename: 'internal-pdf-viewer',
description: 'Portable Document Format'
},
{
name: 'Chrome PDF Viewer',
filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai',
description: ''
},
{
name: 'Native Client',
filename: 'internal-nacl-plugin',
description: ''
}
],
fns: {
namedItem: instanceName => {
// Returns the Plugin/MimeType with the specified name.
const fn = function (name) {
if (!arguments.length) {
throw new TypeError(
`Failed to execute 'namedItem' on '${instanceName}': 1 argument required, but only 0 present.`
)
}
return this[name] || null
}
mockedFns.push({ ref: fn, name: 'namedItem' })
return fn
},
item: instanceName => {
// Returns the Plugin/MimeType at the specified index into the array.
const fn = function (index) {
if (!arguments.length) {
throw new TypeError(
`Failed to execute 'namedItem' on '${instanceName}': 1 argument required, but only 0 present.`
)
}
return this[index] || null
}
mockedFns.push({ ref: fn, name: 'item' })
return fn
},
refresh: instanceName => {
// Refreshes all plugins on the current page, optionally reloading documents.
const fn = function () {
return undefined
}
mockedFns.push({ ref: fn, name: 'refresh' })
return fn
}
}
}
// Poor mans _.pluck
const getSubset = (keys, obj) =>
keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {})
function generateMimeTypeArray () {
const arr = fakeData.mimeTypes
.map(obj => getSubset(['type', 'suffixes', 'description'], obj))
.map(obj => Object.setPrototypeOf(obj, MimeType.prototype))
arr.forEach(obj => {
arr[obj.type] = obj
})
// Mock functions
arr.namedItem = fakeData.fns.namedItem('MimeTypeArray')
arr.item = fakeData.fns.item('MimeTypeArray')
return Object.setPrototypeOf(arr, MimeTypeArray.prototype)
}
const mimeTypeArray = generateMimeTypeArray()
Object.defineProperty(navigator, 'mimeTypes', {
get: () => mimeTypeArray
})
function generatePluginArray () {
const arr = fakeData.plugins
.map(obj => getSubset(['name', 'filename', 'description'], obj))
.map(obj => {
const mimes = fakeData.mimeTypes.filter(
m => m.__pluginName === obj.name
)
// Add mimetypes
mimes.forEach((mime, index) => {
navigator.mimeTypes[mime.type].enabledPlugin = obj
obj[mime.type] = navigator.mimeTypes[mime.type]
obj[index] = navigator.mimeTypes[mime.type]
})
obj.length = mimes.length
return obj
})
.map(obj => {
// Mock functions
obj.namedItem = fakeData.fns.namedItem('Plugin')
obj.item = fakeData.fns.item('Plugin')
return obj
})
.map(obj => Object.setPrototypeOf(obj, Plugin.prototype))
arr.forEach(obj => {
arr[obj.name] = obj
})
// Mock functions
arr.namedItem = fakeData.fns.namedItem('PluginArray')
arr.item = fakeData.fns.item('PluginArray')
arr.refresh = fakeData.fns.refresh('PluginArray')
return Object.setPrototypeOf(arr, PluginArray.prototype)
}
const pluginArray = generatePluginArray()
Object.defineProperty(navigator, 'plugins', {
get: () => pluginArray
})
// Make mockedFns toString() representation resemble a native function
makeFnsNative(mockedFns)
}
try {
const isPluginArray = navigator.plugins instanceof PluginArray
const hasPlugins = isPluginArray && navigator.plugins.length > 0
if (isPluginArray && hasPlugins) {
return // nothing to do here
}
mockPluginsAndMimeTypes()
} catch (err) {}
})

@@ -26,2 +219,4 @@ }

module.exports = function (pluginConfig) { return new Plugin(pluginConfig) }
module.exports = function (pluginConfig) {
return new Plugin(pluginConfig)
}

@@ -9,7 +9,9 @@ ## API

### [Plugin](https://github.com/berstend/puppeteer-extra/blob/db57ea66cf10d407cf63af387892492e495a84f2/packages/puppeteer-extra-plugin-stealth/evasions/navigator.plugins/index.js#L8-L23)
### [Plugin](https://git@github.com/:berstend/puppeteer-extra/blob/ff112879545e8e68d6500d731ceeafc22d187dd3/packages/puppeteer-extra-plugin-stealth/evasions/navigator.plugins/index.js#L10-L216)
**Extends: PuppeteerExtraPlugin**
Pass the Plugins Length Test.
In headless mode `navigator.mimeTypes` and `navigator.plugins` are empty.
This plugin quite emulates both of these to match regular headful Chrome.
We even go so far as to mock functional methods, instance types and `.toString` properties. :D

@@ -16,0 +18,0 @@ Type: `function (opts)`

@@ -7,13 +7,21 @@ 'use strict'

* Pass the Webdriver Test.
* Will delete `navigator.webdriver` property.
*/
class Plugin extends PuppeteerExtraPlugin {
constructor (opts = { }) { super(opts) }
constructor (opts = {}) {
super(opts)
}
get name () { return 'stealth/evasions/navigator.webdriver' }
get name () {
return 'stealth/evasions/navigator.webdriver'
}
async onPageCreated (page) {
// Chrome returns undefined, Firefox false
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => false
})
// eslint-disable-next-line
const newProto = navigator.__proto__
delete newProto.webdriver
// eslint-disable-next-line
navigator.__proto__ = newProto
})

@@ -23,2 +31,4 @@ }

module.exports = function (pluginConfig) { return new Plugin(pluginConfig) }
module.exports = function (pluginConfig) {
return new Plugin(pluginConfig)
}

@@ -9,3 +9,3 @@ ## API

### [Plugin](https://github.com/berstend/puppeteer-extra/blob/db57ea66cf10d407cf63af387892492e495a84f2/packages/puppeteer-extra-plugin-stealth/evasions/navigator.webdriver/index.js#L8-L20)
### [Plugin](https://git@github.com/:berstend/puppeteer-extra/blob/ff112879545e8e68d6500d731ceeafc22d187dd3/packages/puppeteer-extra-plugin-stealth/evasions/navigator.webdriver/index.js#L9-L28)

@@ -15,2 +15,3 @@ **Extends: PuppeteerExtraPlugin**

Pass the Webdriver Test.
Will delete `navigator.webdriver` property.

@@ -17,0 +18,0 @@ Type: `function (opts)`

@@ -9,3 +9,3 @@ ## API

### [Plugin](https://github.com/berstend/puppeteer-extra/blob/db57ea66cf10d407cf63af387892492e495a84f2/packages/puppeteer-extra-plugin-stealth/evasions/user-agent/index.js#L13-L19)
### [Plugin](https://git@github.com/:berstend/puppeteer-extra/blob/ff112879545e8e68d6500d731ceeafc22d187dd3/packages/puppeteer-extra-plugin-stealth/evasions/user-agent/index.js#L13-L19)

@@ -12,0 +12,0 @@ **Extends: PuppeteerExtraPlugin**

@@ -35,6 +35,2 @@ 'use strict'

*
* ### Notes
* Word of caution: Due to the intrusive nature of these detection mitigation techniques
* they might break functionality on certain sites. Selectively disable techniques if that happens or submit a PR with a fix. :-)
*
* ### Kudos

@@ -51,5 +47,6 @@ * Thanks to [Evan Sangaline](https://intoli.com/blog/not-possible-to-block-chrome-headless/) and [Paul Irish](https://github.com/paulirish/headless-cat-n-mouse) for kickstarting the discussion!

* const puppeteer = require('puppeteer-extra')
* // Enable stealth plugin
* // Enable stealth plugin with all evasions
* puppeteer.use(require('puppeteer-extra-plugin-stealth')())
*
*
* ;(async () => {

@@ -78,5 +75,9 @@ * // Launch the browser in headless mode and set up a page.

class Plugin extends PuppeteerExtraPlugin {
constructor (opts = { }) { super(opts) }
constructor (opts = {}) {
super(opts)
}
get name () { return 'stealth' }
get name () {
return 'stealth'
}

@@ -92,2 +93,4 @@ get defaults () {

'iframe.contentWindow',
'window.outerdimensions',
'webgl.vendor',
'user-agent'

@@ -108,4 +111,4 @@ ])

get dependencies () {
return new Set([...this.opts.enabledEvasions]
.map(e => `${this.name}/evasions/${e}`)
return new Set(
[...this.opts.enabledEvasions].map(e => `${this.name}/evasions/${e}`)
)

@@ -126,3 +129,5 @@ }

*/
get availableEvasions () { return this.defaults.availableEvasions }
get availableEvasions () {
return this.defaults.availableEvasions
}

@@ -142,3 +147,5 @@ /**

*/
get enabledEvasions () { return this.opts.enabledEvasions }
get enabledEvasions () {
return this.opts.enabledEvasions
}

@@ -148,5 +155,9 @@ /**

*/
set enabledEvasions (evasions) { this.opts.enabledEvasions = evasions }
set enabledEvasions (evasions) {
this.opts.enabledEvasions = evasions
}
}
module.exports = function (pluginConfig) { return new Plugin(pluginConfig) }
module.exports = function (pluginConfig) {
return new Plugin(pluginConfig)
}
{
"name": "puppeteer-extra-plugin-stealth",
"version": "2.1.1",
"version": "2.1.2",
"description": "Stealth mode: Applies various techniques to make detection of headless puppeteer harder.",

@@ -40,3 +40,3 @@ "main": "index.js",

},
"gitHead": "2783eda8b71df3eb3e360614302c08007d467628"
"gitHead": "72fe830c158f1e971c8499fdd5232338dd53c220"
}
# puppeteer-extra-plugin-stealth
> A plugin for [puppeteer-extra](https://github.com/berstend/puppeteer-extra).
> A plugin for [puppeteer-extra](https://github.com/berstend/puppeteer-extra) to prevent detection.

@@ -9,4 +9,53 @@ ### Install

yarn add puppeteer-extra-plugin-stealth
# - or -
npm install puppeteer-extra-plugin-stealth
```
### Usage
```js
const puppeteer = require("puppeteer-extra")
const pluginStealth = require("puppeteer-extra-plugin-stealth")
puppeteer.use(pluginStealth())
```
## Changelog
### `v2.1.2`
- Improved: `navigator.plugins` - we fully emulate plugins/mimetypes in headless now 🎉
- New: `webgl.vendor` - is otherwise set to "Google" in headless
- New: `window.outerdimensions` - fix missing window.outerWidth/outerHeight and viewport
- Fixed: `navigator.webdriver` now returns undefined instead of false
## Test results (red is bad)
#### Vanilla puppeteer <strong>without stealth 😢</strong>
<table class="image">
<tr>
<td><figure class="image"><a href="./stealthtests/_results/headless-chromium-vanilla.js.png"><img src="./stealthtests/_results/_thumbs/headless-chromium-vanilla.js.png"></a><figcaption>Chromium + headless</figcaption></figure></td>
<td><figure class="image"><a href="./stealthtests/_results/headful-chromium-vanilla.js.png"><img src="./stealthtests/_results/_thumbs/headful-chromium-vanilla.js.png"></a><figcaption>Chromium + headful</figcaption></figure></td>
<td><figure class="image"><a href="./stealthtests/_results/headless-chrome-vanilla.js.png"><img src="./stealthtests/_results/_thumbs/headless-chrome-vanilla.js.png"></a><figcaption>Chrome + headless</figcaption></figure></td>
<td><figure class="image"><a href="./stealthtests/_results/headful-chrome-vanilla.js.png"><img src="./stealthtests/_results/_thumbs/headful-chrome-vanilla.js.png"></a><figcaption>Chrome + headful</figcaption></figure></td>
</tr>
</table>
#### Puppeteer <strong>with stealth plugin 💯</strong>
<table class="image">
<tr>
<td><figure class="image"><a href="./stealthtests/_results/headless-chromium-stealth.js.png"><img src="./stealthtests/_results/_thumbs/headless-chromium-stealth.js.png"></a><figcaption>Chromium + headless</figcaption></figure></td>
<td><figure class="image"><a href="./stealthtests/_results/headful-chromium-stealth.js.png"><img src="./stealthtests/_results/_thumbs/headful-chromium-stealth.js.png"></a><figcaption>Chromium + headful</figcaption></figure></td>
<td><figure class="image"><a href="./stealthtests/_results/headless-chrome-stealth.js.png"><img src="./stealthtests/_results/_thumbs/headless-chrome-stealth.js.png"></a><figcaption>Chrome + headless</figcaption></figure></td>
<td><figure class="image"><a href="./stealthtests/_results/headful-chrome-stealth.js.png"><img src="./stealthtests/_results/_thumbs/headful-chrome-stealth.js.png"></a><figcaption>Chrome + headful</figcaption></figure></td>
</tr>
</table>
Tests have been done using [this test site](https://bot.sannysoft.com/) and [these scripts](./stealthtests/).
## API

@@ -22,3 +71,2 @@

- [Contributing](#contributing)
- [Notes](#notes)
- [Kudos](#kudos)

@@ -28,3 +76,3 @@ - [availableEvasions](#availableevasions)

### [Plugin](https://github.com/berstend/puppeteer-extra/blob/db57ea66cf10d407cf63af387892492e495a84f2/packages/puppeteer-extra-plugin-stealth/index.js#L75-L142)
### [Plugin](https://git@github.com/:berstend/puppeteer-extra/blob/ff112879545e8e68d6500d731ceeafc22d187dd3/packages/puppeteer-extra-plugin-stealth/index.js#L72-L151)

@@ -65,7 +113,2 @@ **Extends: PuppeteerExtraPlugin**

#### Notes
Word of caution: Due to the intrusive nature of these detection mitigation techniques
they might break functionality on certain sites. Selectively disable techniques if that happens or submit a PR with a fix. :-)
#### Kudos

@@ -86,5 +129,6 @@

const puppeteer = require('puppeteer-extra')
// Enable stealth plugin
// Enable stealth plugin with all evasions
puppeteer.use(require('puppeteer-extra-plugin-stealth')())
;(async () => {

@@ -111,3 +155,3 @@ // Launch the browser in headless mode and set up a page.

#### [availableEvasions](https://github.com/berstend/puppeteer-extra/blob/db57ea66cf10d407cf63af387892492e495a84f2/packages/puppeteer-extra-plugin-stealth/index.js#L121-L121)
#### [availableEvasions](https://git@github.com/:berstend/puppeteer-extra/blob/ff112879545e8e68d6500d731ceeafc22d187dd3/packages/puppeteer-extra-plugin-stealth/index.js#L124-L126)

@@ -130,3 +174,3 @@ Get all available evasions.

#### [enabledEvasions](https://github.com/berstend/puppeteer-extra/blob/db57ea66cf10d407cf63af387892492e495a84f2/packages/puppeteer-extra-plugin-stealth/index.js#L136-L136)
#### [enabledEvasions](https://git@github.com/:berstend/puppeteer-extra/blob/ff112879545e8e68d6500d731ceeafc22d187dd3/packages/puppeteer-extra-plugin-stealth/index.js#L141-L143)

@@ -133,0 +177,0 @@ Get all enabled evasions.

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc