Skip to content

Instantly share code, notes, and snippets.

@binsarjr
Created June 4, 2022 02:39
Show Gist options
  • Save binsarjr/e62540e7dd4d35a83281bda0de427d49 to your computer and use it in GitHub Desktop.
Save binsarjr/e62540e7dd4d35a83281bda0de427d49 to your computer and use it in GitHub Desktop.
const headlessDetected = () => {
alert("Automation detected")
}
// Test for user agent
function testUserAgent() {
const userAgent = ['phantomjs', 'Headless']
let agent = navigator.userAgent;
return new RegExp(userAgent.join('|'), 'ig').test(agent);
}
// Test for app version (almost equal to user agent)
function testAppVersion() {
let appVersion = navigator.appVersion;
return /headless/i.test(appVersion);
}
// Test for webdriver (headless browser has this flag true)
function testWebdriver() {
let webdriver = navigator.webdriver;
return Boolean(webdriver);
}
// Test for stack trace
function testStackTrace(headless = 'headless') {
let err
try {
null[0]()
} catch (e) {
err = e
} finally {
return err.stack.indexOf(headless) > -1
}
}
// Test for permission
async function testPermission() {
let permissionStatus, notificationPermission;
if (!navigator.permissions) {
return false;
}
permissionStatus = await navigator.permissions.query({ name: "notifications" });
notificationPermission = Notification.permission;
return notificationPermission === "denied" && permissionStatus.state === "prompt"
}
// Test for connection-rtt
function testConnectionRtt() {
let connection = navigator.connection;
let connectionRtt = connection ? connection.rtt : undefined;
if (connectionRtt === undefined) {
return false;
} else {
return connectionRtt === 0;
}
}
// Test for devtools protocol
function testDevtool() {
const any = /./;
let count = 0;
let oldToString = any.toString;
any.toString = function () {
count++;
return "any";
}
let usingDevTools = count > 1;
any.toString = oldToString;
return usingDevTools
}
// Test for languages
function testLanguages() {
let language = navigator.language;
let languagesLength = navigator.languages.length;
return !language || languagesLength === 0
}
// Tests for mime types prototype
function testMimePrototype() {
let correctPrototypes = MimeTypeArray.prototype === navigator.mimeTypes.__proto__;
if (navigator.mimeTypes.length > 0)
correctPrototypes &= MimeType.prototype === navigator.mimeTypes[0].__proto__;
return !correctPrototypes;
}
// Test for plugins
function testPlugins() {
return !navigator.plugins instanceof PluginArray || navigator.plugins.length == 0
}
// Tests for plugins prototype
function testPluginsPrototype() {
let correctPrototypes = PluginArray.prototype === navigator.plugins.__proto__;
if (navigator.plugins.length > 0)
correctPrototypes &= Plugin.prototype === navigator.plugins[0].__proto__;
return !correctPrototypes;
}
function testPhantomJS() {
const callPhantom = () => Boolean(window.callPhantom || window._phantom)
return testStackTrace('phantomjs') || callPhantom()
}
// Test for time elapsed after alert(). If it's closed too fast (< 30ms), it means
// the browser is headless
function testTimeElapse() {
let start = Date.now();
alert("Tekan OK");
let elapse = Date.now() - start;
return elapse < 30;
}
async function test() {
if (testUserAgent()) return headlessDetected()
if (testAppVersion()) return headlessDetected()
if (testWebdriver()) return headlessDetected()
if (testStackTrace()) return headlessDetected()
if (await testPermission()) return headlessDetected()
if (testConnectionRtt()) return headlessDetected()
if (testDevtool()) return headlessDetected()
if (testLanguages()) return headlessDetected()
if (testMimePrototype()) return headlessDetected()
if (testPlugins()) return headlessDetected()
if (testPluginsPrototype()) return headlessDetected()
if (testPhantomJS()) return headlessDetected()
if (testTimeElapse()) return headlessDetected()
}
; (async function () {
await test()
if (!Function.prototype.bind) {
detected()
}
if (Function.prototype.bind.toString().replace(/bind/g, 'Error') != Error.toString()) {
detected()
}
if (Function.prototype.toString.toString().replace(/toString/g, 'Error') != Error.toString()) {
detected()
}
})()

currently only doing analysis when using phantomjs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment