Rename to hkt.sh

This commit is contained in:
mango
2026-03-21 01:10:53 +08:00
parent 76a263d0f9
commit 8f1171fe99
6676 changed files with 1724268 additions and 0 deletions

View File

@@ -0,0 +1,162 @@
const test = require('ava')
const { vanillaPuppeteer, addExtra, compareLooseVersionStrings } = require('./util')
const Plugin = require('..')
// Fix CI issues with old versions
const isOldPuppeteerVersion = () => {
const version = process.env.PUPPETEER_VERSION
const isOld = version && (version === '1.9.0' || version === '1.6.2')
return isOld
}
/* global HTMLIFrameElement */
/* global Notification */
test('stealth: will pass Paul Irish', async t => {
const browser = await addExtra(vanillaPuppeteer)
.use(Plugin())
.launch({ headless: true })
const page = await browser.newPage()
await page.exposeFunction('compareLooseVersionStrings', compareLooseVersionStrings)
const detectionResults = await page.evaluate(detectHeadless)
await browser.close()
if (isOldPuppeteerVersion()) {
t.true(true)
return
}
const wasHeadlessDetected = Object.values(detectionResults).some(Boolean)
if (wasHeadlessDetected) {
console.log(detectionResults)
}
t.false(wasHeadlessDetected)
})
async function detectHeadless() {
const results = {}
async function test(name, fn) {
const detectionPassed = await fn()
if (detectionPassed) console.log(`Chrome headless detected via ${name}`)
results[name] = detectionPassed
}
await test('userAgent', _ => {
return /HeadlessChrome/.test(window.navigator.userAgent)
})
// navigator.webdriver behavior change since release 89.0.4339.0. See also #448
if (await compareLooseVersionStrings(navigator.userAgent, '89.0.4339.0') >= 0) {
await test('navigator.webdriver is not false', _ => {
return navigator.webdriver !== false
})
} else {
// Detects the --enable-automation || --headless flags
// Will return true in headful if --enable-automation is provided
await test('navigator.webdriver present', _ => {
return 'webdriver' in navigator
})
await test('navigator.webdriver not undefined', _ => {
return navigator.webdriver !== undefined
})
/* eslint-disable no-proto */
await test('navigator.webdriver property overridden', _ => {
return (
Object.getOwnPropertyDescriptor(navigator.__proto__, 'webdriver') !==
undefined
)
})
await test('navigator.webdriver prop detected', _ => {
for (const prop in navigator) {
if (prop === 'webdriver') {
return true
}
}
return false
})
}
await test('window.chrome missing', _ => {
return /Chrome/.test(window.navigator.userAgent) && !window.chrome
})
await test('permissions API', async _ => {
const permissionStatus = await navigator.permissions.query({
name: 'notifications'
})
return (
Notification.permission === 'denied' &&
permissionStatus.state === 'prompt'
)
})
await test('permissions API overriden', _ => {
const permissions = window.navigator.permissions
if (permissions.query.toString() !== 'function query() { [native code] }')
return true
if (
permissions.query.toString.toString() !==
'function toString() { [native code] }'
)
return true
if (
permissions.query.toString.hasOwnProperty('[[Handler]]') && // eslint-disable-line
permissions.query.toString.hasOwnProperty('[[Target]]') && // eslint-disable-line
permissions.query.toString.hasOwnProperty('[[IsRevoked]]') // eslint-disable-line
)
return true
if (permissions.hasOwnProperty('query')) return true // eslint-disable-line
})
await test('navigator.plugins empty', _ => {
return navigator.plugins.length === 0
})
await test('navigator.languages blank', _ => {
return navigator.languages === ''
})
await test('iFrame for fresh window object', _ => {
// evaluateOnNewDocument scripts don't apply within [srcdoc] (or [sandbox]) iframes
// https://github.com/GoogleChrome/puppeteer/issues/1106#issuecomment-359313898
const iframe = document.createElement('iframe')
iframe.srcdoc = 'page intentionally left blank'
document.body.appendChild(iframe)
// Verify iframe prototype isn't touched
const descriptors = Object.getOwnPropertyDescriptors(
HTMLIFrameElement.prototype
)
if (
descriptors.contentWindow.get.toString() !==
'function get contentWindow() { [native code] }'
)
return true
// Verify iframe isn't remapped to main window
if (iframe.contentWindow === window) return true
// Here we would need to rerun all tests with `iframe.contentWindow` as `window`
// Example:
return iframe.contentWindow.navigator.plugins.length === 0
})
// This detects that a devtools protocol agent is attached.
// So it will also pass true in headful Chrome if the devtools window is attached
await test('toString', _ => {
let gotYou = 0
const spooky = /./
spooky.toString = function() {
gotYou++
return 'spooky'
}
console.debug(spooky)
return gotYou > 1
})
return results
}

View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>title foo</title>
<!-- Testing evasions with a real html page makes things easier -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
console.log('ServiceWorker registration failed: ', err);
});
});
}
</script>
</head>
<body>
<h1>Test page with service worker</h1>
</body>
</html>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>title foo</title>
<!-- Testing evasions with a real html page makes things easier -->
</head>
<body>
<h1>Test page</h1>
</body>
</html>

View File

@@ -0,0 +1 @@
// Left empty

View File

@@ -0,0 +1,52 @@
const test = require('ava')
const fpscanner = require('fpscanner')
const { getVanillaFingerPrint, getStealthFingerPrint, compareLooseVersionStrings } = require('./util')
const Plugin = require('../.')
// Fix CI issues with old versions
const isOldPuppeteerVersion = () => {
const version = process.env.PUPPETEER_VERSION
if (!version) {
return false
}
if (version === '1.9.0' || version === '1.6.2') {
return true
}
return false
}
test('vanilla: will fail multiple fpscanner tests', async t => {
const fingerPrint = await getVanillaFingerPrint()
const testedFingerPrints = fpscanner.analyseFingerprint(fingerPrint)
const failedChecks = Object.values(testedFingerPrints).filter(
val => val.consistent < 3
)
if (isOldPuppeteerVersion()) {
t.is(failedChecks.length, 8)
} else {
t.is(failedChecks.length, 7)
}
})
test('stealth: will not fail a single fpscanner test', async t => {
const fingerPrint = await getStealthFingerPrint(Plugin)
const testedFingerPrints = fpscanner.analyseFingerprint(fingerPrint)
const failedChecks = Object.values(testedFingerPrints).filter(
val => val.consistent < 3
)
if (failedChecks.length) {
console.warn('The following fingerprints failed:', failedChecks)
}
if (compareLooseVersionStrings(fingerPrint.userAgent, '89.0.4339.0') >= 0) {
// Updated navigator.webdriver behavior breaks the fpscanner tests.
t.is(failedChecks.length, 1)
t.is(failedChecks[0].name, 'WEBDRIVER')
} else {
t.is(failedChecks.length, 0)
}
})

View File

@@ -0,0 +1,112 @@
const test = require('ava')
const { vanillaPuppeteer, addExtra } = require('./util')
const Plugin = require('..')
const http = require('http')
const fs = require('fs')
const path = require('path')
// Create a simple HTTP server. Service Workers cannot be served from file:// URIs
const httpServer = async () => {
const server = await http
.createServer((req, res) => {
let contents, type
if (req.url === '/sw.js') {
contents = fs.readFileSync(path.join(__dirname, './fixtures/sw.js'))
type = 'application/javascript'
} else {
contents = fs.readFileSync(
path.join(__dirname, './fixtures/dummy-with-service-worker.html')
)
type = 'text/html'
}
res.setHeader('Content-Type', type)
res.writeHead(200)
res.end(contents)
})
.listen(0) // random free port
return `http://127.0.0.1:${server.address().port}/`
}
let browser, page, worker
test.before(async t => {
const address = await httpServer()
console.log(`Server is running on port ${address}`)
browser = await addExtra(vanillaPuppeteer)
.use(Plugin())
.launch({ headless: true })
page = await browser.newPage()
worker = new Promise(resolve => {
browser.on('targetcreated', async target => {
if (target.type() === 'service_worker') {
resolve(target.worker())
}
})
})
await page.goto(address)
worker = await worker
})
test.after(async t => {
await browser.close()
})
test.skip('stealth: inconsistencies between page and worker', async t => {
const pageFP = await page.evaluate(detectFingerprint)
const workerFP = await worker.evaluate(detectFingerprint)
t.deepEqual(pageFP, workerFP)
})
test.serial.skip('stealth: creepjs has good trust score', async t => {
page.goto('https://abrahamjuliot.github.io/creepjs/')
const score = await (
await (
await page.waitForSelector('#fingerprint-data .unblurred')
).getProperty('textContent')
).jsonValue()
t.true(
parseInt(score) > 80,
`The creepjs score is: ${parseInt(score)}% but it should be at least 80%`
)
})
/* global OffscreenCanvas */
function detectFingerprint() {
const results = {}
const props = [
'userAgent',
'language',
'hardwareConcurrency',
'deviceMemory',
'languages',
'platform'
]
props.forEach(el => {
results[el] = navigator[el].toString()
})
const canvasOffscreenWebgl = new OffscreenCanvas(256, 256)
const contextWebgl = canvasOffscreenWebgl.getContext('webgl')
const rendererInfo = contextWebgl.getExtension('WEBGL_debug_renderer_info')
results.webglVendor = contextWebgl.getParameter(
rendererInfo.UNMASKED_VENDOR_WEBGL
)
results.webglRenderer = contextWebgl.getParameter(
rendererInfo.UNMASKED_RENDERER_WEBGL
)
results.timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
return results
}

View File

@@ -0,0 +1,65 @@
const assert = require('assert')
const vanillaPuppeteer = require('puppeteer')
const { addExtra } = require('puppeteer-extra')
const fpCollectPath = require.resolve('fpcollect/dist/fpCollect.min.js')
const getFingerPrintFromPage = async page => {
return page.evaluate(() => fpCollect.generateFingerprint()) // eslint-disable-line
}
const dummyHTMLPath = require('path').join(__dirname, './fixtures/dummy.html')
const getFingerPrint = async (puppeteer, pageFn) => {
const browser = await puppeteer.launch({ headless: true })
const page = await browser.newPage()
await page.goto('file://' + dummyHTMLPath)
await page.addScriptTag({ path: fpCollectPath })
const fingerPrint = await getFingerPrintFromPage(page)
let pageFnResult = null
if (pageFn) {
pageFnResult = await pageFn(page)
}
await browser.close()
return { ...fingerPrint, pageFnResult }
}
const getVanillaFingerPrint = async pageFn =>
getFingerPrint(vanillaPuppeteer, pageFn)
const getStealthFingerPrint = async (Plugin, pageFn, pluginOptions = null) =>
getFingerPrint(addExtra(vanillaPuppeteer).use(Plugin(pluginOptions)), pageFn)
// Expecting the input string to be in one of these formats:
// - The UA string
// - The shorter version string from Puppeteers browser.version()
// - The shortest four-integer string
const parseLooseVersionString = looseVersionString => looseVersionString
.match(/(\d+\.){3}\d+/)[0]
.split('.')
.map(x => parseInt(x))
const compareLooseVersionStrings = (version0, version1) => {
const parsed0 = parseLooseVersionString(version0)
const parsed1 = parseLooseVersionString(version1)
assert(parsed0.length == 4)
assert(parsed1.length == 4)
for (let i = 0; i < parsed0.length; i++) {
if (parsed0[i] < parsed1[i]) {
return -1
} else if (parsed0[i] > parsed1[i]) {
return 1
}
}
return 0
}
module.exports = {
getVanillaFingerPrint,
getStealthFingerPrint,
dummyHTMLPath,
vanillaPuppeteer,
addExtra,
compareLooseVersionStrings
}