Stealth mode plugin for puppeteer-extra that applies various techniques to make detection of headless browsers harder.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Evasion techniques that fix window dimension and iframe-related detection methods. These evasions address inconsistencies in window properties and iframe behavior that can reveal headless browser automation.
Fixes missing window.outerWidth and window.outerHeight properties in headless mode.
// Evasion name: 'window.outerdimensions'
// Fixes: window.outerWidth, window.outerHeight, and viewport consistencyThis evasion:
window.outerWidth and window.outerHeight values in headless modeDetection Method Prevented:
// This detection method will be fooled:
if (window.outerWidth === 0 || window.outerHeight === 0) {
// Would detect headless mode (missing outer dimensions)
} else if (window.outerWidth <= window.innerWidth) {
// Would detect unusual dimension relationship
} else {
// Assumes normal browser with proper window chrome
}Usage Examples:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
// Enable window dimensions evasion
const windowStealth = StealthPlugin({
enabledEvasions: new Set(['window.outerdimensions'])
});
puppeteer.use(windowStealth);
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
// Window dimensions will now be realistic
await page.evaluate(() => {
console.log('Inner:', window.innerWidth, 'x', window.innerHeight);
console.log('Outer:', window.outerWidth, 'x', window.outerHeight);
// Outer dimensions will be larger than inner (accounting for browser chrome)
console.log('Has window chrome:', window.outerWidth > window.innerWidth);
// Returns: true (like a real browser)
});Fixes iframe detection issues, particularly with srcdoc powered iframes.
// Evasion name: 'iframe.contentWindow'
// Fixes: iframe.contentWindow.chrome detection and related iframe issuesThis evasion:
srcdoc iframesDetection Method Prevented:
// This detection method will be fooled:
const iframe = document.createElement('iframe');
iframe.srcdoc = '<html><body></body></html>';
document.body.appendChild(iframe);
iframe.onload = () => {
if (iframe.contentWindow.chrome) {
// Assumes real Chrome browser
} else {
// Would detect headless/automation through missing Chrome object in iframe
}
};Implementation Details:
The iframe content window evasion:
Usage Examples:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
// Enable iframe evasion along with Chrome API evasions
const iframeStealth = StealthPlugin({
enabledEvasions: new Set([
'iframe.contentWindow',
'chrome.runtime',
'chrome.app'
])
});
puppeteer.use(iframeStealth);
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Test iframe behavior
await page.evaluate(() => {
const iframe = document.createElement('iframe');
iframe.srcdoc = `
<html>
<body>
<script>
// This will now work properly without detection
console.log('Chrome in iframe:', typeof chrome !== 'undefined');
console.log('Chrome runtime:', typeof chrome?.runtime !== 'undefined');
</script>
</body>
</html>
`;
document.body.appendChild(iframe);
return new Promise(resolve => {
iframe.onload = () => {
// iframe content window now has proper Chrome objects
resolve(typeof iframe.contentWindow.chrome !== 'undefined');
};
});
});The window outer dimensions evasion automatically adjusts the browser viewport to create realistic dimension relationships:
// The evasion automatically calculates appropriate dimensions
// Inner dimensions: Set by user or browser defaults
// Outer dimensions: Inner + realistic browser chrome size
const chromeSize = 85; // Typical browser chrome height
const outerWidth = innerWidth + 16; // Small width addition for scrollbars
const outerHeight = innerHeight + chromeSize; // Browser chrome heightIf you set a custom viewport, the evasion respects your settings:
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Set custom viewport - evasion will adjust outer dimensions accordingly
await page.setViewport({ width: 1366, height: 768 });
// Outer dimensions will be calculated based on this viewport
// outerWidth: ~1382, outerHeight: ~853Both window and iframe evasions ensure consistency across different execution contexts:
// Main frame and all iframes will have consistent Chrome object availability
await page.evaluate(() => {
// Main frame
console.log('Main chrome:', typeof chrome);
const iframe = document.createElement('iframe');
iframe.srcdoc = '<script>parent.postMessage(typeof chrome, "*")</script>';
window.addEventListener('message', (e) => {
console.log('Iframe chrome:', e.data);
// Both will show 'object' if Chrome evasions are enabled
});
document.body.appendChild(iframe);
});Window evasions apply to all windows opened from the page:
await page.evaluate(() => {
const newWindow = window.open('about:blank');
// New window will also have proper outer dimensions
setTimeout(() => {
console.log('New window outer:', newWindow.outerWidth, newWindow.outerHeight);
console.log('New window chrome:', typeof newWindow.chrome);
newWindow.close();
}, 100);
});Window and frame evasions are designed for:
// Only fix window dimensions, not iframe behavior
const windowOnly = StealthPlugin({
enabledEvasions: new Set(['window.outerdimensions'])
});
// Only fix iframe behavior, keep original window dimensions
const iframeOnly = StealthPlugin({
enabledEvasions: new Set(['iframe.contentWindow', 'chrome.runtime'])
});Window and frame evasions work best when combined with related evasions:
// Comprehensive window/frame protection
const windowFrameStealth = StealthPlugin({
enabledEvasions: new Set([
'window.outerdimensions', // Fix window dimensions
'iframe.contentWindow', // Fix iframe behavior
'chrome.runtime', // Provide Chrome objects in iframes
'chrome.app', // Complete Chrome API simulation
'navigator.webdriver' // Remove automation indicators
])
});Install with Tessl CLI
npx tessl i tessl/npm-puppeteer-extra-plugin-stealth