import getOfflineAudioContext, { audioHTML } from './audio'
import getCanvas2d, { canvasHTML } from './canvas'
import getCSS, { cssHTML } from './css'
import getCSSMedia, { cssMediaHTML } from './cssmedia'
import getHTMLElementVersion, { htmlElementVersionHTML } from './document'
import getClientRects, { clientRectsHTML } from './domrect'
import getConsoleErrors, { consoleErrorsHTML } from './engine'
import { timer, getCapturedErrors, caniuse, errorsHTML, attempt } from './errors'
import getEngineFeatures, { featuresHTML, getFeaturesLie } from './features'
import getFonts, { fontsHTML } from './fonts'
import getHeadlessFeatures, { headlessFeaturesHTML } from './headless'
import getIntl, { intlHTML } from './intl'
import { getLies, PARENT_PHANTOM, liesHTML, PROTO_BENCHMARK } from './lies'
import getMaths, { mathsHTML } from './math'
import getMedia, { mediaHTML } from './media'
import getNavigator, { navigatorHTML } from './navigator'
import getPrediction, { getBlankIcons, predictionErrorPatch, renderPrediction } from './prediction'
import getResistance, { resistanceHTML } from './resistance'
import renderSamples, { getSamples, getRawFingerprint } from './samples'
import getScreen, { screenHTML } from './screen'
import getVoices, { voicesHTML } from './speech'
import { getStatus, getStorage, statusHTML } from './status'
import getSVG, { svgHTML } from './svg'
import getTimezone, { timezoneHTML } from './timezone'
import { getTrash, trashHTML } from './trash'
import { hashify, hashMini, getBotHash, getFuzzyHash, cipher } from './utils/crypto'
import { exile, getStackBytes, getTTFB, measure } from './utils/exile'
import { IS_BLINK, braveBrowser, getBraveMode, getBraveUnprotectedParameters, computeWindowsRelease, hashSlice, ENGINE_IDENTIFIER, getUserAgentRestored, attemptWindows11UserAgent, LowerEntropy, queueTask, Analysis } from './utils/helpers'
import { patch, html, getDiffs, modal, HTMLNote } from './utils/html'
import getCanvasWebgl, { webglHTML } from './webgl'
import getWebRTCData, { getWebRTCDevices, webrtcHTML } from './webrtc'
import getWindowFeatures, { windowFeaturesHTML } from './window'
import getBestWorkerScope, { Scope, spawnWorker, workerScopeHTML } from './worker'
!async function() {
'use strict';
const scope = await spawnWorker()
if (scope == Scope.WORKER) {
return
}
await queueTask()
const stackBytes = getStackBytes()
const [, measuredTime, ttfb] = await Promise.all([
exile(),
measure(),
getTTFB(),
])
console.clear()
const measured = (outerWidth - innerWidth < 150) && (outerHeight - innerHeight < 150) ? measuredTime : 0
const isBrave = IS_BLINK ? await braveBrowser() : false
const braveMode = isBrave ? getBraveMode() : {}
const braveFingerprintingBlocking = isBrave && (braveMode.standard || braveMode.strict)
const fingerprint = async () => {
const timeStart = timer()
const fingerprintTimeStart = timer()
// @ts-ignore
const [
workerScopeComputed,
voicesComputed,
offlineAudioContextComputed,
canvasWebglComputed,
canvas2dComputed,
windowFeaturesComputed,
htmlElementVersionComputed,
cssComputed,
cssMediaComputed,
screenComputed,
mathsComputed,
consoleErrorsComputed,
timezoneComputed,
clientRectsComputed,
fontsComputed,
mediaComputed,
svgComputed,
resistanceComputed,
intlComputed,
] = await Promise.all([
getBestWorkerScope(),
getVoices(),
getOfflineAudioContext(),
getCanvasWebgl(),
getCanvas2d(),
getWindowFeatures(),
getHTMLElementVersion(),
getCSS(),
getCSSMedia(),
getScreen(),
getMaths(),
getConsoleErrors(),
getTimezone(),
getClientRects(),
getFonts(),
getMedia(),
getSVG(),
getResistance(),
getIntl(),
]).catch((error) => console.error(error.message))
const navigatorComputed = await getNavigator(workerScopeComputed)
.catch((error) => console.error(error.message))
// @ts-ignore
const [
headlessComputed,
featuresComputed,
] = await Promise.all([
getHeadlessFeatures({
webgl: canvasWebglComputed,
workerScope: workerScopeComputed,
}),
getEngineFeatures({
cssComputed,
navigatorComputed,
windowFeaturesComputed,
}),
]).catch((error) => console.error(error.message))
// @ts-ignore
const [
liesComputed,
trashComputed,
capturedErrorsComputed,
] = await Promise.all([
getLies(),
getTrash(),
getCapturedErrors(),
]).catch((error) => console.error(error.message))
const fingerprintTimeEnd = fingerprintTimeStart()
console.log(`Fingerprinting complete in ${(fingerprintTimeEnd).toFixed(2)}ms`)
// GPU Prediction
const { parameters: gpuParameter } = canvasWebglComputed || {}
const reducedGPUParameters = {
...(
braveFingerprintingBlocking ? getBraveUnprotectedParameters(gpuParameter) :
gpuParameter
),
RENDERER: undefined,
SHADING_LANGUAGE_VERSION: undefined,
UNMASKED_RENDERER_WEBGL: undefined,
UNMASKED_VENDOR_WEBGL: undefined,
VERSION: undefined,
VENDOR: undefined,
}
// Hashing
const hashStartTime = timer()
// @ts-ignore
const [
windowHash,
headlessHash,
htmlHash,
cssMediaHash,
cssHash,
styleHash,
styleSystemHash,
screenHash,
voicesHash,
canvas2dHash,
canvas2dImageHash,
canvas2dPaintHash,
canvas2dTextHash,
canvas2dEmojiHash,
canvasWebglHash,
canvasWebglImageHash,
canvasWebglParametersHash,
pixelsHash,
pixels2Hash,
mathsHash,
consoleErrorsHash,
timezoneHash,
rectsHash,
domRectHash,
audioHash,
fontsHash,
workerHash,
mediaHash,
mimeTypesHash,
navigatorHash,
liesHash,
trashHash,
errorsHash,
svgHash,
resistanceHash,
intlHash,
featuresHash,
deviceOfTimezoneHash,
] = await Promise.all([
hashify(windowFeaturesComputed),
hashify(headlessComputed),
hashify((htmlElementVersionComputed || {}).keys),
hashify(cssMediaComputed),
hashify(cssComputed),
hashify((cssComputed || {}).computedStyle),
hashify((cssComputed || {}).system),
hashify(screenComputed),
hashify(voicesComputed),
hashify(canvas2dComputed),
hashify((canvas2dComputed || {}).dataURI),
hashify((canvas2dComputed || {}).paintURI),
hashify((canvas2dComputed || {}).textURI),
hashify((canvas2dComputed || {}).emojiURI),
hashify(canvasWebglComputed),
hashify((canvasWebglComputed || {}).dataURI),
hashify(reducedGPUParameters),
((canvasWebglComputed || {}).pixels || []).length ? hashify(canvasWebglComputed.pixels) : undefined,
((canvasWebglComputed || {}).pixels2 || []).length ? hashify(canvasWebglComputed.pixels2) : undefined,
hashify((mathsComputed || {}).data),
hashify((consoleErrorsComputed || {}).errors),
hashify(timezoneComputed),
hashify(clientRectsComputed),
hashify([
(clientRectsComputed || {}).elementBoundingClientRect,
(clientRectsComputed || {}).elementClientRects,
(clientRectsComputed || {}).rangeBoundingClientRect,
(clientRectsComputed || {}).rangeClientRects,
]),
hashify(offlineAudioContextComputed),
hashify(fontsComputed),
hashify(workerScopeComputed),
hashify(mediaComputed),
hashify((mediaComputed || {}).mimeTypes),
hashify(navigatorComputed),
hashify(liesComputed),
hashify(trashComputed),
hashify(capturedErrorsComputed),
hashify(svgComputed),
hashify(resistanceComputed),
hashify(intlComputed),
hashify(featuresComputed),
hashify((() => {
const {
bluetoothAvailability,
device,
deviceMemory,
hardwareConcurrency,
maxTouchPoints,
oscpu,
platform,
system,
userAgentData,
} = navigatorComputed || {}
const {
architecture,
bitness,
mobile,
model,
platform: uaPlatform,
platformVersion,
} = userAgentData || {}
const { 'any-pointer': anyPointer } = cssMediaComputed?.mediaCSS || {}
const { colorDepth, pixelDepth, height, width } = screenComputed || {}
const { location, locationEpoch, zone } = timezoneComputed || {}
const {
deviceMemory: deviceMemoryWorker,
hardwareConcurrency: hardwareConcurrencyWorker,
gpu,
platform: platformWorker,
system: systemWorker,
timezoneLocation: locationWorker,
userAgentData: userAgentDataWorker,
} = workerScopeComputed || {}
const { compressedGPU, confidence } = gpu || {}
const {
architecture: architectureWorker,
bitness: bitnessWorker,
mobile: mobileWorker,
model: modelWorker,
platform: uaPlatformWorker,
platformVersion: platformVersionWorker,
} = userAgentDataWorker || {}
return [
anyPointer,
architecture,
architectureWorker,
bitness,
bitnessWorker,
bluetoothAvailability,
colorDepth,
...(compressedGPU && confidence != 'low' ? [compressedGPU] : []),
device,
deviceMemory,
deviceMemoryWorker,
hardwareConcurrency,
hardwareConcurrencyWorker,
height,
location,
locationWorker,
locationEpoch,
maxTouchPoints,
mobile,
mobileWorker,
model,
modelWorker,
oscpu,
pixelDepth,
platform,
platformWorker,
platformVersion,
platformVersionWorker,
system,
systemWorker,
uaPlatform,
uaPlatformWorker,
width,
zone,
]
})()),
]).catch((error) => console.error(error.message))
// console.log(performance.now()-start)
const hashTimeEnd = hashStartTime()
const timeEnd = timeStart()
console.log(`Hashing complete in ${(hashTimeEnd).toFixed(2)}ms`)
if (PARENT_PHANTOM) {
// @ts-ignore
PARENT_PHANTOM.parentNode.removeChild(PARENT_PHANTOM)
}
const fingerprint = {
workerScope: !workerScopeComputed ? undefined : { ...workerScopeComputed, $hash: workerHash},
navigator: !navigatorComputed ? undefined : {...navigatorComputed, $hash: navigatorHash},
windowFeatures: !windowFeaturesComputed ? undefined : {...windowFeaturesComputed, $hash: windowHash},
headless: !headlessComputed ? undefined : {...headlessComputed, $hash: headlessHash},
htmlElementVersion: !htmlElementVersionComputed ? undefined : {...htmlElementVersionComputed, $hash: htmlHash},
cssMedia: !cssMediaComputed ? undefined : {...cssMediaComputed, $hash: cssMediaHash},
css: !cssComputed ? undefined : {...cssComputed, $hash: cssHash},
screen: !screenComputed ? undefined : {...screenComputed, $hash: screenHash},
voices: !voicesComputed ? undefined : {...voicesComputed, $hash: voicesHash},
media: !mediaComputed ? undefined : {...mediaComputed, $hash: mediaHash},
canvas2d: !canvas2dComputed ? undefined : {...canvas2dComputed, $hash: canvas2dHash},
canvasWebgl: !canvasWebglComputed ? undefined : {...canvasWebglComputed, pixels: pixelsHash, pixels2: pixels2Hash, $hash: canvasWebglHash},
maths: !mathsComputed ? undefined : {...mathsComputed, $hash: mathsHash},
consoleErrors: !consoleErrorsComputed ? undefined : {...consoleErrorsComputed, $hash: consoleErrorsHash},
timezone: !timezoneComputed ? undefined : {...timezoneComputed, $hash: timezoneHash},
clientRects: !clientRectsComputed ? undefined : {...clientRectsComputed, $hash: rectsHash},
offlineAudioContext: !offlineAudioContextComputed ? undefined : {...offlineAudioContextComputed, $hash: audioHash},
fonts: !fontsComputed ? undefined : {...fontsComputed, $hash: fontsHash},
lies: !liesComputed ? undefined : {...liesComputed, $hash: liesHash},
trash: !trashComputed ? undefined : {...trashComputed, $hash: trashHash},
capturedErrors: !capturedErrorsComputed ? undefined : {...capturedErrorsComputed, $hash: errorsHash},
svg: !svgComputed ? undefined : {...svgComputed, $hash: svgHash },
resistance: !resistanceComputed ? undefined : {...resistanceComputed, $hash: resistanceHash},
intl: !intlComputed ? undefined : {...intlComputed, $hash: intlHash},
features: !featuresComputed ? undefined : {...featuresComputed, $hash: featuresHash},
}
return {
fingerprint,
styleSystemHash,
styleHash,
domRectHash,
mimeTypesHash,
canvas2dImageHash,
canvasWebglImageHash,
canvas2dPaintHash,
canvas2dTextHash,
canvas2dEmojiHash,
canvasWebglParametersHash,
deviceOfTimezoneHash,
timeEnd,
}
}
// fingerprint and render
const [
{
fingerprint: fp,
styleSystemHash,
styleHash,
domRectHash,
mimeTypesHash,
canvas2dImageHash,
canvas2dPaintHash,
canvas2dTextHash,
canvas2dEmojiHash,
canvasWebglImageHash,
canvasWebglParametersHash,
deviceOfTimezoneHash,
timeEnd,
},
sQuota,
] = await Promise.all([
fingerprint().catch((error) => console.error(error)) || {},
getStorage(),
])
if (!fp) {
throw new Error('Fingerprint failed!')
}
const tmSum = +(fp.canvas2d?.textMetricsSystemSum) || 0
const glBc = Analysis.webglBrandCapabilities
// š² Dragon fire
if ((({
'01299ea5': 1688108400000,
'a2217a02': 1688108400000,
'632ecc1d': 1688108400000,
'520916bb': 1684998000000,
})[hashMini([stackBytes, tmSum])] || +new Date()) > +new Date()) {
try {
const meta = document.createElement('meta')
meta.httpEquiv = 'refresh'
meta.content = `1;${atob('YWJvdXQ6Ymxhbms=')}`
document.head.appendChild(meta)
} catch {}
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
await new Promise((_) => { })
}
console.log('%cā loose fingerprint passed', 'color:#4cca9f')
console.groupCollapsed('Loose Fingerprint')
console.log(fp)
console.groupEnd()
console.groupCollapsed('Loose Fingerprint JSON')
console.log('diff check at https://www.diffchecker.com/diff\n\n', JSON.stringify(fp, null, '\t'))
console.groupEnd()
// Trusted Fingerprint
const trashLen = fp.trash.trashBin.length
const liesLen = !('totalLies' in fp.lies) ? 0 : fp.lies.totalLies
const errorsLen = fp.capturedErrors.data.length
const hardenEntropy = (workerScope, prop) => {
return (
!workerScope ? prop :
(workerScope.localeEntropyIsTrusty && workerScope.localeIntlEntropyIsTrusty) ? prop :
undefined
)
}
const privacyResistFingerprinting = (
fp.resistance && /^(tor browser|firefox)$/i.test(fp.resistance.privacy)
)
// harden gpu
const hardenGPU = (canvasWebgl) => {
const { gpu: { confidence, compressedGPU } } = canvasWebgl
return (
confidence == 'low' ? {} : {
UNMASKED_RENDERER_WEBGL: compressedGPU,
UNMASKED_VENDOR_WEBGL: canvasWebgl.parameters.UNMASKED_VENDOR_WEBGL,
}
)
}
const creep = {
navigator: (
!fp.navigator || fp.navigator.lied ? undefined : {
bluetoothAvailability: fp.navigator.bluetoothAvailability,
device: fp.navigator.device,
deviceMemory: fp.navigator.deviceMemory,
hardwareConcurrency: fp.navigator.hardwareConcurrency,
maxTouchPoints: fp.navigator.maxTouchPoints,
oscpu: fp.navigator.oscpu,
platform: fp.navigator.platform,
system: fp.navigator.system,
userAgentData: {
...(fp.navigator.userAgentData || {}),
// loose
brandsVersion: undefined,
uaFullVersion: undefined,
},
vendor: fp.navigator.vendor,
}
),
screen: (
!fp.screen || fp.screen.lied || privacyResistFingerprinting || LowerEntropy.SCREEN ? undefined :
hardenEntropy(
fp.workerScope, {
height: fp.screen.height,
width: fp.screen.width,
pixelDepth: fp.screen.pixelDepth,
colorDepth: fp.screen.colorDepth,
lied: fp.screen.lied,
},
)
),
workerScope: !fp.workerScope || fp.workerScope.lied ? undefined : {
deviceMemory: (
braveFingerprintingBlocking ? undefined : fp.workerScope.deviceMemory
),
hardwareConcurrency: (
braveFingerprintingBlocking ? undefined : fp.workerScope.hardwareConcurrency
),
// system locale in blink
language: !LowerEntropy.TIME_ZONE ? fp.workerScope.language : undefined,
platform: fp.workerScope.platform,
system: fp.workerScope.system,
device: fp.workerScope.device,
timezoneLocation: (
!LowerEntropy.TIME_ZONE ?
hardenEntropy(fp.workerScope, fp.workerScope.timezoneLocation) :
undefined
),
webglRenderer: (
(fp.workerScope.gpu.confidence != 'low') ? fp.workerScope.gpu.compressedGPU : undefined
),
webglVendor: (
(fp.workerScope.gpu.confidence != 'low') ? fp.workerScope.webglVendor : undefined
),
userAgentData: {
...fp.workerScope.userAgentData,
// loose
brandsVersion: undefined,
uaFullVersion: undefined,
},
},
media: fp.media,
canvas2d: ((canvas2d) => {
if (!canvas2d) {
return
}
const { lied, liedTextMetrics } = canvas2d
let data
if (!lied) {
const { dataURI, paintURI, textURI, emojiURI } = canvas2d
data = {
lied,
...{ dataURI, paintURI, textURI, emojiURI },
}
}
if (!liedTextMetrics) {
const { textMetricsSystemSum, emojiSet } = canvas2d
data = {
...(data || {}),
...{ textMetricsSystemSum, emojiSet },
}
}
return data
})(fp.canvas2d),
canvasWebgl: (!fp.canvasWebgl || fp.canvasWebgl.lied || LowerEntropy.WEBGL) ? undefined : (
braveFingerprintingBlocking ? {
parameters: {
...getBraveUnprotectedParameters(fp.canvasWebgl.parameters),
...hardenGPU(fp.canvasWebgl),
},
} : {
...((gl, canvas2d) => {
if ((canvas2d && canvas2d.lied) || LowerEntropy.CANVAS) {
// distrust images
const { extensions, gpu, lied, parameterOrExtensionLie } = gl
return {
extensions,
gpu,
lied,
parameterOrExtensionLie,
}
}
return gl
})(fp.canvasWebgl, fp.canvas2d),
parameters: {
...fp.canvasWebgl.parameters,
...hardenGPU(fp.canvasWebgl),
},
}
),
cssMedia: !fp.cssMedia ? undefined : {
reducedMotion: caniuse(() => fp.cssMedia.mediaCSS['prefers-reduced-motion']),
colorScheme: (
braveFingerprintingBlocking ? undefined :
caniuse(() => fp.cssMedia.mediaCSS['prefers-color-scheme'])
),
monochrome: caniuse(() => fp.cssMedia.mediaCSS.monochrome),
invertedColors: caniuse(() => fp.cssMedia.mediaCSS['inverted-colors']),
forcedColors: caniuse(() => fp.cssMedia.mediaCSS['forced-colors']),
anyHover: caniuse(() => fp.cssMedia.mediaCSS['any-hover']),
hover: caniuse(() => fp.cssMedia.mediaCSS.hover),
anyPointer: caniuse(() => fp.cssMedia.mediaCSS['any-pointer']),
pointer: caniuse(() => fp.cssMedia.mediaCSS.pointer),
colorGamut: caniuse(() => fp.cssMedia.mediaCSS['color-gamut']),
screenQuery: (
privacyResistFingerprinting || (LowerEntropy.SCREEN || LowerEntropy.IFRAME_SCREEN) ?
undefined :
hardenEntropy(fp.workerScope, caniuse(() => fp.cssMedia.screenQuery))
),
},
css: !fp.css ? undefined : fp.css.system.fonts,
timezone: !fp.timezone || fp.timezone.lied || LowerEntropy.TIME_ZONE ? undefined : {
locationMeasured: hardenEntropy(fp.workerScope, fp.timezone.locationMeasured),
lied: fp.timezone.lied,
},
offlineAudioContext: !fp.offlineAudioContext ? undefined : (
fp.offlineAudioContext.lied || LowerEntropy.AUDIO ? undefined :
fp.offlineAudioContext
),
fonts: !fp.fonts || fp.fonts.lied || LowerEntropy.FONTS ? undefined : fp.fonts.fontFaceLoadFonts,
forceRenew: 1682918207897,
}
console.log('%cā stable fingerprint passed', 'color:#4cca9f')
console.groupCollapsed('Stable Fingerprint')
console.log(creep)
console.groupEnd()
console.groupCollapsed('Stable Fingerprint JSON')
console.log('diff check at https://www.diffchecker.com/diff\n\n', JSON.stringify(creep, null, '\t'))
console.groupEnd()
const [fpHash, creepHash] = await Promise.all([hashify(fp), hashify(creep)]).catch((error) => {
console.error(error.message)
}) || []
// session
const computeSession = ({ fingerprint, loading = false, computePreviousLoadRevision = false }) => {
const data = {
revisedKeysFromPreviousLoad: [],
revisedKeys: [],
initial: '',
loads: 0,
}
try {
const currentFingerprint = Object.keys(fingerprint).reduce((acc, key) => {
if (!fingerprint[key]) {
return acc
}
acc[key] = fingerprint[key].$hash
return acc
}, {})
// @ts-ignore
const loads = +(sessionStorage.getItem('loads'))
// @ts-ignore
const initialFingerprint = JSON.parse(sessionStorage.getItem('initialFingerprint'))
// @ts-ignore
const previousFingerprint = JSON.parse(sessionStorage.getItem('previousFingerprint'))
if (initialFingerprint) {
data.initial = hashMini(initialFingerprint)
if (loading) {
data.loads = 1+loads
sessionStorage.setItem('loads', ''+data.loads)
} else {
data.loads = loads
}
if (computePreviousLoadRevision) {
sessionStorage.setItem('previousFingerprint', JSON.stringify(currentFingerprint))
}
const currentFingerprintKeys = Object.keys(currentFingerprint)
const revisedKeysFromPreviousLoad = currentFingerprintKeys
.filter((key) => currentFingerprint[key] != previousFingerprint[key])
const revisedKeys = currentFingerprintKeys
.filter((key) => currentFingerprint[key] != initialFingerprint[key])
// @ts-ignore
data.revisedKeys = revisedKeys.length ? revisedKeys : []
// @ts-ignore
data.revisedKeysFromPreviousLoad = revisedKeysFromPreviousLoad.length ? revisedKeysFromPreviousLoad : []
return data
}
sessionStorage.setItem('initialFingerprint', JSON.stringify(currentFingerprint))
sessionStorage.setItem('previousFingerprint', JSON.stringify(currentFingerprint))
sessionStorage.setItem('loads', ''+1)
data.initial = hashMini(currentFingerprint)
data.loads = 1
return data
} catch (error) {
console.error(error)
return data
}
}
const blankFingerprint = '0000000000000000000000000000000000000000000000000000000000000000'
const el = document.getElementById('fingerprint-data')
patch(el, html`
Loading...
trust score: 100%
visits: 1
first: ##/##/####, 00:00:00 AM
alive: 0.0 hrs
auto-delete in
shadow: 0.00000
trash (0): none
lies (0): none
errors (0): none
session (0): 00000000
revisions (0): 00000000
loose fp (0): 00000000
bot: 0:friend:00000
idle min-max: 0.000-0.000 hrs
performance benchmark: 0.00 ms
Loading...
network: 000.000
tokens: 500
hidden fingerprint: none
org:
Mars Communications
MR Mars Union (Launchville)
intensity: 0.0000
Loading...
${getBlankIcons()}
${getBlankIcons()}self
${getBlankIcons()}system styles
${getBlankIcons()}computed styles
${getBlankIcons()}html element
${getBlankIcons()}js runtime
${getBlankIcons()}js engine
${getBlankIcons()}domRect emojis
${getBlankIcons()}domRect
${getBlankIcons()}svg emojis
${getBlankIcons()}mimeTypes
${getBlankIcons()}audio
${getBlankIcons()}canvas image
${getBlankIcons()}canvas paint
${getBlankIcons()}canvas text
${getBlankIcons()}canvas emoji
${getBlankIcons()}textMetrics
${getBlankIcons()}webgl
${getBlankIcons()}gpu params
${getBlankIcons()}gpu model
${getBlankIcons()}fonts
${getBlankIcons()}voices
${getBlankIcons()}screen
${getBlankIcons()}resistance
${getBlankIcons()}device of timezone
WebRTC
host connection:
candidate:0000000000 1 udp 9353978903 93549af7-47d4-485c-a57a-751a3d213876.local 56518 typ host generation 0 ufrag bk84 network-cost 999
foundation/ip:
0000000000
000.000.000.000
capabilities:
stun connection:
candidate:0000000000 1 udp 9353978903 93549af7-47d4-485c-a57a-751a3d213876.local 56518 typ host generation 0 ufrag bk84 network-cost 999
devices (0):
mic, audio, webcam
${timezoneHTML(fp)}
${intlHTML(fp)}
${headlessFeaturesHTML(fp)}
${resistanceHTML(fp)}
${workerScopeHTML(fp)}
${webglHTML(fp)}
${screenHTML(fp)}
${canvasHTML(fp)}
${fontsHTML(fp)}
${clientRectsHTML(fp)}
${svgHTML(fp)}
${audioHTML(fp)}
${voicesHTML(fp)}
${mediaHTML(fp)}
${featuresHTML(fp)}
${cssMediaHTML(fp)}
${cssHTML(fp)}
${mathsHTML(fp)}
${consoleErrorsHTML(fp)}
${windowFeaturesHTML(fp)}
${htmlElementVersionHTML(fp)}
${navigatorHTML(fp)}
`, async () => {
// send analysis fingerprint
Promise.all([
getWebRTCData(),
getWebRTCDevices(),
getStatus(),
]).then(async (data) => {
const [webRTC, mediaDevices, status] = data || []
patch(document.getElementById('webrtc-connection'), html`
${webrtcHTML(webRTC, mediaDevices)}
`)
patch(document.getElementById('status-info'), html`
${statusHTML(status)}
`)
// entropy
const RAW_BODY = {
...getRawFingerprint(fp),
memory: status?.memory,
memoryGB: status?.memoryInGigabytes,
quota: status?.quota,
quotaIsInsecure: status?.quotaIsInsecure,
quotaGB: status?.quotaInGigabytes,
stackBytes,
stackSize: status?.stackSize,
timingRes: status?.timingRes,
tmSum,
rtt: status?.rtt,
networkType: status?.downlink ? [status?.effectiveType, status?.type] : undefined,
webRTCFoundation: webRTC?.foundation,
webRTCCodecs: (
webRTC?.codecsSdp ? (await hashify(webRTC.codecsSdp)).slice(0, 16) :
undefined
),
webRTCMediaDevices: mediaDevices,
scripts: status?.scripts,
client: status?.clientLitter,
scriptSize: status?.scriptSize,
benchmark: Math.floor(timeEnd || 0),
benchmarkProto: PROTO_BENCHMARK,
measured,
ttfb,
}
// console.log(`'`+Object.keys(RAW_BODY).join(`',\n'`))
cipher(RAW_BODY).then((secret) => {
fetch('https://creepjs-api.web.app/analysis', {
method: 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json',
},
body: JSON.stringify(secret),
})
.then(async (res) => {
if (res.ok) {
const data = await res.json()
const { host, protocol } = location
const isSecure = /https:/.test(protocol)
const shouldDiscourage = isSecure && !(
host == 'abrahamjuliot.github.io' ||
/github(preview|)\.dev$/.test(host)
)
if (shouldDiscourage || data.isNetworkAbuse) {
window.location.href = 'about:blank'
return
}
const { network, org, tokensAvailable, tag, intensity } = data
const TagGrade: Record = {
1: 'scale-down grade-D',
2: 'scale-down grade-F',
}
const TagName: Record = {
1: 'sus',
2: 'bad',
}
patch(document.getElementById('network-analysis'), html`
Analysis
network: ${network}
tokens: ${tokensAvailable}
hidden fingerprint: ${
TagName[tag] || HTMLNote.SECRET
}
org:
${org ? org.split(':').join('
') : HTMLNote.UNKNOWN}
intensity: ${intensity}
`)
// expose results to the window
// @ts-expect-error does not exist
window.Fingerprint = JSON.parse(JSON.stringify(fp))
// @ts-expect-error does not exist
window.Creep = JSON.parse(JSON.stringify(creep))
console.groupCollapsed('Analysis')
console.log(JSON.stringify(data, null, '\t'))
console.groupEnd()
}
}) // return the analysis
.catch((error) => console.error(error))
})
}).catch((err) => console.error(err))
// fetch fingerprint data from server
const id = 'creep-browser'
const visitorElem = document.getElementById(id)
const { botHash, badBot } = getBotHash(fp, { getFeaturesLie, computeWindowsRelease })
const fuzzyFingerprint = await getFuzzyHash(fp)
const { $hash, privacy, mode, extension } = fp.resistance || {}
const resistanceSet = new Set([hashSlice($hash), privacy, mode, extension])
resistanceSet.delete(undefined)
const resistanceType = [...resistanceSet].join(':')
const fetchVisitorDataTimer = timer()
let status = ''
const secret = await cipher({
id: creepHash,
subId: fpHash,
trashLen,
liesLen,
errorsLen,
fuzzy: fuzzyFingerprint,
botHash,
perf: (timeEnd || 0).toFixed(2),
resistance: resistanceType,
stackBytes,
tmSum,
glBc,
sQuota,
measured,
ttfb,
canvasHash: fp.canvas2d?.lied === true ? null : fp.canvas2d?.$hash.slice(0, 8),
webglHash: fp.canvasWebgl?.lied === true ? null : fp.canvasWebgl?.$hash.slice(0, 8),
screenHash: fp.screen?.$hash.slice(0, 8),
timeZoneHash: fp.timezone?.$hash.slice(0, 8),
})
fetch('https://creepjs-api.web.app/fp', {
method: 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json',
},
body: JSON.stringify(secret),
})
.then((res) => {
if (res.ok) return res.json()
status = `${res.status}:${res.statusText.toLocaleLowerCase()}`
return res
})
.then(async (data) => {
console.groupCollapsed('Server Response')
console.log(JSON.stringify(data, null, '\t'))
fetchVisitorDataTimer('response time')
console.groupEnd()
const {
fingerprint: serverFingerprint,
firstVisit,
// lastVisit: latestVisit,
// lastVisitEpoch,
timeHoursAlive: persistence,
// looseFingerprints: subIds,
visits,
looseSwitchCount: switchCount,
// hasTrash,
// hasLied,
// hasErrors,
signature,
fuzzyInit,
fuzzyLast,
shadow,
shadowBits,
score,
scoreData,
crowdBlendingScore: fpCrowdBlendingScore,
bot,
botHash,
botLevel,
timeHoursIdleMin,
timeHoursIdleMax,
benchmark,
resistance: resistanceId,
traced,
supervised,
timeSeries,
} = data || {}
const shouldLock = badBot || traced > 1
const fuzzyFpEl = document.getElementById('fuzzy-fingerprint')
const fuzzyDiff = getDiffs({
stringA: fuzzyInit,
stringB: fuzzyLast,
charDiff: true,
decorate: (diff) => `${diff}`,
})
patch(fuzzyFpEl, html`
Fuzzy: ${fuzzyInit || blankFingerprint}
Diffs: ${fuzzyDiff || blankFingerprint}
`)
// Display fingerprint
const rand = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1) + min)
setTimeout(() => {
const isTracing = traced > 0
const timeseries = `timeseries ${timeSeries}`
const hardenedFingerprint = serverFingerprint !== creepHash ? serverFingerprint : creepHash
const displayedFingerprint = isTracing ? hardenedFingerprint.slice(0, 64 - timeseries.length - 2) : hardenedFingerprint
patch(document.getElementById('creep-fingerprint'), html`
FP ID: ${displayedFingerprint?.split('').map((x: string, i: number) => {
return `${x}`
}).join('')}${isTracing ? `[${timeseries}]` : ''}
`)
}, 300)
const toLocaleStr = (str: string) => {
const date = new Date(str)
const dateString = date.toLocaleDateString()
const timeString = date.toLocaleTimeString()
return `${dateString}, ${timeString}`
}
const {
switchCountPointGain,
errorsPointGain,
trashPointGain,
liesPointGain,
tracedPointGain,
measuredPointGain,
supervisedPointGain,
shadowBitsPointGain,
grade,
} = JSON.parse(scoreData)
const computePoints = (x) => {
return `${
x > 0 ? `+${x}` : x < 0 ? `${x}` : ''
}`
}
const renewedDate = new Date(creep.forceRenew)
const renewedDateString = `${renewedDate.getMonth()+1}/${renewedDate.getDate()}/${renewedDate.getFullYear()}`
const addDays = (date, n) => {
const d = new Date(date)
d.setDate(d.getDate() + n)
return d
}
const shouldStyle = (renewedDateString) => {
const endNoticeDate = addDays(renewedDateString, 7)
const daysRemaining = Math.round((+endNoticeDate - +new Date()) / (1000 * 3600 * 24))
return daysRemaining >= 0
}
const getChunks = (list, chunkLen) => list.reduce((acc, x, i) => {
const chunk = Math.floor(i/chunkLen)
acc[chunk] = [...(acc[chunk]||[]), x]
return acc
}, [])
const styleChunks = (chunks) => chunks.map((y, yi) => {
const animate = (n) => `animation: balloon ${3*n}00ms ${6*n}00ms cubic-bezier(.47,.47,.56,1.26) alternate infinite`
return `${
y.map((x, xi) => ``).join('')
}
`
}).join('')
const { initial, loads, revisedKeys } = computeSession({ fingerprint: fp, loading: true })
const template = `
fingerprints renewed ${
new Date(renewedDateString).toLocaleDateString()
}
${resistanceId}
Browser
trust score:
${score}% ${grade}
visits: ${visits} ${
supervised > 0 ? `[supervised] ${computePoints(supervisedPointGain)}` : ''
}
first: ${toLocaleStr(firstVisit)}
alive: ${((hours) => {
const format = (n) => {
const fixed = n.toFixed(1)
const shouldMakeNumberWhole = /\.0/.test(fixed)
return shouldMakeNumberWhole ? n.toFixed() : fixed
}
return (
hours > 48 ? `${format(hours/24)} days` : `${format(hours)} hrs`
)
})(persistence)} ${
traced > 0 ? `[traced] ${computePoints(tracedPointGain)}` : ''
}
auto-delete in
shadow: ${!shadowBits ? '0' : shadowBits.toFixed(5)} ${computePoints(shadowBitsPointGain)}
${
!shadowBits ? '' : `${hashMini(shadow)}`
}
${styleChunks(getChunks(shadow.split(''), 8))}
${trashHTML(fp, computePoints(trashPointGain))}
${liesHTML(fp, computePoints(liesPointGain))}
${errorsHTML(fp, computePoints(errorsPointGain))}
session (${''+loads}):${initial} ${computePoints(measuredPointGain)}
revisions (${''+revisedKeys.length}): ${
!revisedKeys.length ? 'none' : modal(
`creep-revisions`,
revisedKeys.join('
'),
hashMini(revisedKeys),
)
}
loose fp (${''+switchCount}):${hashSlice(fpHash)} ${computePoints(switchCountPointGain)}
bot: ${bot.toFixed(2)}:${botLevel}:${botHash}
idle min-max: ${timeHoursIdleMin}-${timeHoursIdleMax} hrs
performance benchmark: ${benchmark.toFixed(2)} ms
${
signature ?
`
` :
`
`
}
`
patch(visitorElem, html`${template}`, () => {
// show self destruct time
const el = document.getElementById('auto-delete')
const arrivalTime = +new Date
const showTime = () => {
requestAnimationFrame(showTime)
const hoursInMs = 36e5
const day = hoursInMs * 24
const destructionDate = +new Date(+new Date-(day*30))
const hoursTillSelfDestruct = Math.abs(arrivalTime - destructionDate) / hoursInMs/24
// @ts-ignore
return el.style.setProperty(
'--auto-delete-time',
`'${hoursTillSelfDestruct.toFixed(8)}'`,
)
}
showTime()
// listen to form signature if not already signed
if (signature) {
return
}
const form = document.getElementById('signature')
// @ts-ignore
form.addEventListener('submit', async (event) => {
event.preventDefault()
// @ts-ignore
const input = document.getElementById('signature-input').value
const submit = confirm(`Are you sure? This cannot be undone.\n\nsignature: ${input}`)
if (!submit) {
return
}
const signatureRequest = `https://creepjs-api.web.app/sign?id=${creepHash}&signature=${input}`
// animate out
// @ts-ignore
form.classList.remove('fade-right-in')
// @ts-ignore
form.classList.add('fade-down-out')
// fetch/animate in
return fetch(signatureRequest)
.then((response) => response.json())
.then((data) => {
return setTimeout(() => {
patch(form, html`
`)
return console.log('Signed: ', JSON.stringify(data, null, '\t'))
}, 300)
})
.catch((error) => {
patch(form, html`
`)
return console.error('Error!', error.message)
})
})
})
const {
maths,
consoleErrors,
htmlElementVersion,
windowFeatures,
// css,
clientRects,
offlineAudioContext,
resistance,
canvas2d,
canvasWebgl,
screen: screenFp,
fonts,
voices,
svg,
media,
} = fp || {}
// const { computedStyle, system } = css || {}
const isTorBrowser = resistance.privacy == 'Tor Browser'
const isRFP = resistance.privacy == 'Firefox'
// const isBravePrivacy = resistance.privacy == 'Brave'
const screenMetrics = (
!screenFp || screenFp.lied || isRFP || isTorBrowser ? 'undefined' :
`${screenFp.width}x${screenFp.height}`
)
const {
compressorGainReduction: gain,
sampleSum,
floatFrequencyDataSum: freqSum,
floatTimeDomainDataSum: timeSum,
values: audioValues,
} = offlineAudioContext || {}
const valuesHash = hashMini(audioValues)
const audioMetrics = `${sampleSum}_${gain}_${freqSum}_${timeSum}_${valuesHash}`
const getBestGPUModel = ({ canvasWebgl, workerScope }) => {
const gpuHasGoodConfidence = (data) => {
return (
(data.gpu || {}).confidence &&
(data.gpu.confidence != 'low')
)
}
if (!canvasWebgl || canvasWebgl.parameterOrExtensionLie) {
return 'undefined'
} else if (workerScope && gpuHasGoodConfidence(workerScope)) {
return workerScope.webglRenderer
} else if (canvasWebgl && !canvasWebgl.parameterOrExtensionLie && gpuHasGoodConfidence(canvasWebgl)) {
return ''+((canvasWebgl.parameters || {}).UNMASKED_RENDERER_WEBGL)
}
return 'undefined'
}
const gpuModel = getBestGPUModel({ canvasWebgl, workerScope: fp.workerScope })
let RAW_BODY // store so we can access el
if (!shouldLock) {
// get data from session
// @ts-ignore
let decryptionData = window.sessionStorage && JSON.parse(sessionStorage.getItem('decryptionData'))
const targetMetrics = [
'canvas2d',
'canvasWebgl',
'clientRects',
'consoleErrors',
'css',
'cssMedia',
'fonts',
'htmlElementVersion',
'maths',
'media',
'navigator',
'offlineAudioContext',
'resistance',
'screen',
'svg',
'timezone',
'voices',
'windowFeatures',
'workerScope',
/* disregard metrics not in samples:
capturedErrors,
features,
headless,
intl,
lies,
trash,
*/
]
const { revisedKeysFromPreviousLoad } = computeSession({
fingerprint: fp,
computePreviousLoadRevision: true,
})
// @ts-ignore
const sessionFingerprintRevision = targetMetrics.filter((x) => revisedKeysFromPreviousLoad.includes(x))
const revisionLen = sessionFingerprintRevision.length
// fetch data
const requireNewDecryptionFetch = !decryptionData || revisionLen
console.log(`${revisionLen} revisions: fetching prediction data from ${requireNewDecryptionFetch ? 'server' : 'session'}...`)
if (requireNewDecryptionFetch) {
const sender = {
e: ({
// this just allows us to keep using Math.PI ** -100 results in the database for consistency
80: 1.9275814160560204e-50,
58: 1.9275814160560185e-50,
77: 1.9275814160560206e-50,
} as Record
)[ENGINE_IDENTIFIER] || 0,
l: +new Date('7/1/1113'),
}
const { userAgent, userAgentData } = fp.workerScope || {}
const { platformVersion: fontPlatformVersion } = fp.fonts || {}
const restoredUA = getUserAgentRestored({ userAgent, userAgentData, fontPlatformVersion })
const windows11UA = attemptWindows11UserAgent({
userAgent,
userAgentData,
fontPlatformVersion,
})
const workerScopeUserAgent = restoredUA || windows11UA
if (restoredUA && (restoredUA != userAgent)) {
console.log(`corrected: ${workerScopeUserAgent}`)
}
// cipher
RAW_BODY = {
stackBytes,
tmSum,
sender: `${sender.e}_${sender.l}`,
isTorBrowser,
isRFP,
isBrave,
resistanceId: resistance.$hash,
mathId: maths.$hash,
errorId: consoleErrors.$hash,
htmlId: htmlElementVersion.$hash,
winId: windowFeatures.$hash,
styleId: styleHash,
styleSystemId: styleSystemHash,
emojiId: (
!clientRects || clientRects.lied ? null :
clientRects.domrectSystemSum
),
domRectId: (
!clientRects || clientRects.lied ? null : domRectHash
),
svgId: (
!svg || svg.lied ? null : svg.svgrectSystemSum
),
mimeTypesId: (
!media || media.lied ? null : mimeTypesHash
),
audioId: (
!offlineAudioContext ||
offlineAudioContext.lied ? null : audioMetrics
),
canvasId: (
!canvas2d || canvas2d.lied ? null :
canvas2dImageHash
),
canvasPaintId: (
!canvas2d || canvas2d.lied ? null :
canvas2dPaintHash
),
canvasTextId: (
!canvas2d || canvas2d.lied ? null :
canvas2dTextHash
),
canvasEmojiId: (
!canvas2d || canvas2d.lied ? null :
canvas2dEmojiHash
),
textMetricsId: (
!canvas2d ||
canvas2d.liedTextMetrics ||
((+canvas2d.textMetricsSystemSum) == 0) ? null :
canvas2d.textMetricsSystemSum
),
webglId: (
!canvasWebgl || (canvas2d || {}).lied || canvasWebgl.lied ? null :
canvasWebglImageHash
),
gpuId: (
!canvasWebgl || canvasWebgl.parameterOrExtensionLie ? null :
canvasWebglParametersHash
),
gpu: gpuModel,
fontsId: !fonts || fonts.lied ? null : fonts.$hash,
voicesId: !voices || voices.lied ? null : voices.$hash,
screenId: screenMetrics,
deviceOfTimezoneId: deviceOfTimezoneHash,
ua: workerScopeUserAgent,
}
const secret = await cipher(RAW_BODY)
decryptionData = await fetch('https://creepjs-api.web.app/decrypt', {
method: 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json',
},
body: JSON.stringify(secret),
})
.then((res) => {
if (res.ok) return res.json()
status = `${res.status}:${res.statusText.toLocaleLowerCase()}`
return res
})
.catch((error) => {
console.error(error)
predictionErrorPatch(error)
return
})
if (decryptionData && window.sessionStorage) {
sessionStorage.setItem('decryptionData', JSON.stringify(decryptionData))
}
}
// Crowd-Blending Score
const crowdMap: Record = {
windowVersion: RAW_BODY?.winId,
jsRuntime: RAW_BODY?.mathId,
jsEngine: RAW_BODY?.errorId,
htmlVersion: RAW_BODY?.htmlId,
styleVersion: RAW_BODY?.styleId,
resistance: RAW_BODY?.resistanceId,
styleSystem: RAW_BODY?.styleSystemId,
emojiSystem: RAW_BODY?.emojiId,
domRectSystem: RAW_BODY?.domRectId,
svgSystem: RAW_BODY?.svgId,
mimeTypesSystem: RAW_BODY?.mimeTypesId,
audioSystem: RAW_BODY?.audioId,
canvasSystem: RAW_BODY?.canvasId,
canvasPaintSystem: RAW_BODY?.canvasPaintId,
canvasTextSystem: RAW_BODY?.canvasTextId,
canvasEmojiSystem: RAW_BODY?.canvasEmojiId,
textMetricsSystem: RAW_BODY?.textMetricsId,
webglSystem: RAW_BODY?.webglId,
gpuSystem: RAW_BODY?.gpuId,
gpuModelSystem: RAW_BODY?.gpu,
fontsSystem: RAW_BODY?.fontsId,
voicesSystem: RAW_BODY?.voicesId,
screenSystem: RAW_BODY?.screenId,
deviceOfTimezone: RAW_BODY?.deviceOfTimezoneId,
}
const scoreKeys = Object.keys(crowdMap)
const decryptionDataScores = Object.keys(crowdMap).reduce((acc, key) => {
const { score } = decryptionData[key] || {}
const reporters = (
score == 36 ? 1 :
score == 84 ? 2 :
score == 96 ? 3 :
score == 100 ? 4 :
0
)
// @ts-ignore
acc.metrics = [...(acc.metrics||[]), { key, score: (score||0), reporters }]
// @ts-ignore
acc.scores = [...(acc.scores||[]), (score||0)]
return acc
}, {})
// @ts-ignore
const { metrics: scoreMetrics } = decryptionDataScores
const scoreMetricsMap = Object.keys(scoreMetrics).reduce((acc, key) => {
const scoreMetricData = scoreMetrics[key]
const { score, reporters } = scoreMetricData
acc[scoreMetricData.key] = { score, reporters }
return acc
}, {} as Record)
// Generate Trace Fingerprint
const traceId = !RAW_BODY ? null : await hashify(
scoreKeys.reduce((acc, key) => {
const hasGoodScore = scoreMetricsMap[key].score > 90
if (hasGoodScore) {
acc.push(String(crowdMap[key]))
}
return acc
}, [] as string[]),
)
// @ts-ignore
const blockedOrOpenlyPoisonedMetric = decryptionDataScores.scores.includes(0)
// @ts-ignore
const validScores = decryptionDataScores.scores.filter((n) => !!n)
const crowdBlendingScoreMin = Math.min(...(validScores.length ? validScores : [0]))
const crowdBlendingScore = (
blockedOrOpenlyPoisonedMetric ? (0.75 * crowdBlendingScoreMin) :
crowdBlendingScoreMin
)
console.groupCollapsed(`Crowd-Blending Score: ${crowdBlendingScore}%`)
console.table(scoreMetricsMap)
console.groupEnd()
if (crowdBlendingScore != fpCrowdBlendingScore) {
console.log(`updating crowd-blending score from ${fpCrowdBlendingScore} to ${crowdBlendingScore}`)
const scoreRequest = `https://creepjs-api.web.app/score-crowd-blending?id=${creepHash}&crowdBlendingScore=${crowdBlendingScore}&traceId=${traceId}&stackBytes=${stackBytes}&tmSum=${tmSum}`
fetch(scoreRequest)
.catch((error) => console.error('Failed Score Request', error))
}
renderPrediction({ decryptionData, crowdBlendingScore })
}
const { samples: decryptionSamples, samplesDidLoadFromSession } = await getSamples() || {}
const cleanGPUString = (x: string) => !x ? x : (''+x).replace(/\//g, '')
const {
window: winSamples,
math: mathSamples,
error: errorSamples,
html: htmlSamples,
style: styleSamples,
resistance: resistanceSamples,
styleVersion: styleVersionSamples,
emoji: emojiSamples,
domRect: domRectSamples,
svg: svgSamples,
mimeTypes: mimeTypesSamples,
audio: audioSamples,
canvas: canvasSamples,
canvasPaint: canvasPaintSamples,
canvasText: canvasTextSamples,
canvasEmoji: canvasEmojiSamples,
textMetrics: textMetricsSamples,
webgl: webglSamples,
fonts: fontsSamples,
voices: voicesSamples,
screen: screenSamples,
gpu: gpuSamples,
gpuModel: gpuModelSamples,
deviceOfTimezone: deviceOfTimezoneSamples,
} = decryptionSamples || {}
if (shouldLock && !decryptionSamples) {
predictionErrorPatch('Failed prediction fetch')
}
if (shouldLock && decryptionSamples) {
// Perform Dragon Fire Magic
const decryptionData = {
windowVersion: getPrediction({ hash: (windowFeatures || {}).$hash, data: winSamples }),
jsRuntime: getPrediction({ hash: (maths || {}).$hash, data: mathSamples }),
jsEngine: getPrediction({ hash: (consoleErrors || {}).$hash, data: errorSamples }),
htmlVersion: getPrediction({ hash: (htmlElementVersion || {}).$hash, data: htmlSamples }),
styleVersion: getPrediction({ hash: styleHash, data: styleVersionSamples }),
styleSystem: getPrediction({ hash: styleSystemHash, data: styleSamples }),
resistance: getPrediction({ hash: (resistance || {}).$hash, data: resistanceSamples }),
emojiSystem: getPrediction({
hash: (clientRects || {}).domrectSystemSum,
data: emojiSamples,
}),
domRectSystem: getPrediction({ hash: domRectHash, data: domRectSamples }),
svgSystem: getPrediction({
hash: (svg || {}).svgrectSystemSum,
data: svgSamples,
}),
mimeTypesSystem: getPrediction({ hash: mimeTypesHash, data: mimeTypesSamples }),
audioSystem: getPrediction({ hash: audioMetrics, data: audioSamples }),
canvasSystem: getPrediction({ hash: canvas2dImageHash, data: canvasSamples }),
canvasPaintSystem: getPrediction({ hash: canvas2dPaintHash, data: canvasPaintSamples }),
canvasTextSystem: getPrediction({ hash: canvas2dTextHash, data: canvasTextSamples }),
canvasEmojiSystem: getPrediction({ hash: canvas2dEmojiHash, data: canvasEmojiSamples }),
textMetricsSystem: getPrediction({
hash: (canvas2d || {}).textMetricsSystemSum,
data: textMetricsSamples,
}),
webglSystem: getPrediction({ hash: canvasWebglImageHash, data: webglSamples }),
gpuSystem: getPrediction({ hash: canvasWebglParametersHash, data: gpuSamples }),
gpuModelSystem: getPrediction({ hash: cleanGPUString(gpuModel), data: gpuModelSamples }),
fontsSystem: getPrediction({ hash: (fonts || {}).$hash, data: fontsSamples }),
voicesSystem: getPrediction({ hash: (voices || {}).$hash, data: voicesSamples }),
screenSystem: getPrediction({ hash: screenMetrics, data: screenSamples }),
deviceOfTimezone: getPrediction({ hash: deviceOfTimezoneHash, data: deviceOfTimezoneSamples }),
}
// @ts-ignore
renderPrediction({ decryptionData, bot: true })
}
// render entropy notes
if (decryptionSamples) {
const getEntropy = (hash, data) => {
let classTotal = 0
const metricTotal = Object.keys(data)
.reduce((acc, key) => acc+= data[key].length, 0)
const decryption = Object.keys(data).find((key) => data[key].find((item) => {
if ((item.id == hash) && (item.reporterTrustScore > 36)) {
const trustedSamples = data[key].filter((sample) => {
return (sample.reporterTrustScore > 36)
})
classTotal = trustedSamples.length
return true
}
return false
}))
return {
classTotal,
decryption,
metricTotal,
}
}
const entropyHash: Record = {
window: (windowFeatures || {}).$hash,
math: (maths || {}).$hash,
error: (consoleErrors || {}).$hash,
html: (htmlElementVersion || {}).$hash,
style: styleSystemHash,
resistance: (resistance || {}).$hash,
styleVersion: styleHash,
emoji: (clientRects || {}).domrectSystemSum,
domRect: domRectHash,
svg: (svg || {}).svgrectSystemSum,
mimeTypes: mimeTypesHash,
audio: audioMetrics,
canvas: canvas2dImageHash,
canvasPaint: canvas2dPaintHash,
canvasText: canvas2dTextHash,
canvasEmoji: canvas2dEmojiHash,
textMetrics: (canvas2d || {}).textMetricsSystemSum,
webgl: canvasWebglImageHash,
fonts: (fonts || {}).$hash,
voices: (voices || {}).$hash,
screen: screenMetrics,
gpu: canvasWebglParametersHash,
gpuModel,
deviceOfTimezone: deviceOfTimezoneHash,
}
const entropyDescriptors: Record = {
window: 'window object',
math: 'engine math runtime',
error: 'engine console errors',
html: 'html element',
style: 'system styles',
resistance: 'resistance patterns',
styleVersion: 'computed styles',
emoji: 'domrect emojis',
domRect: 'domrect metrics',
svg: 'svg emojis',
mimeTypes: 'media mimeTypes',
audio: 'audio metrics',
canvas: 'canvas image',
canvasPaint: 'canvas paint',
canvasText: 'canvas text',
canvasEmoji: 'canvas emoji',
textMetrics: 'textMetrics',
webgl: 'webgl image',
fonts: 'system fonts',
voices: 'voices',
screen: 'screen metrics',
gpu: 'webgl parameters',
gpuModel: 'webgl renderer',
deviceOfTimezone: 'device of timezone',
}
Object.keys(decryptionSamples).forEach((key, i) => {
const hash = (
key == 'gpuModel' ? cleanGPUString(decodeURIComponent(entropyHash[key])) :
entropyHash[key]
)
const {
classTotal,
decryption,
// metricTotal,
} = getEntropy(hash, decryptionSamples[key])
const el = document.getElementById(`${key}-entropy`)
const deviceMetric = (
(key == 'screen') || (key == 'fonts') || (key == 'gpuModel') || (key == 'deviceOfTimezone')
)
const uniquePercent = !classTotal ? 0 : (1/classTotal)*100
const signal = (
uniquePercent == 0 ? 'entropy-unknown' :
uniquePercent < 1 ? 'entropy-high' :
uniquePercent > 10 ? 'entropy-low' :
''
)
const animate = samplesDidLoadFromSession ? '' : `style="animation: fade-up .3s ${100*i}ms ease both;"`
return patch(el, html`
${(uniquePercent).toFixed(2)}%
`)
})
}
return renderSamples(decryptionSamples, { fp, styleSystemHash })
}).catch((error) => {
fetchVisitorDataTimer('Error fetching visitor data')
const el = document.getElementById('browser-detection')
console.error('Error!', error.message)
if (!el) {
return
}
const loader = document.getElementById('loader')
loader && patch(loader, html`
${status}:API access denied
`)
return patch(el, html`
${status}:API access denied
`)
})
})
}()