@stltio/stealth
Advanced tools
Comparing version 0.2.1 to 0.3.0
48
index.js
@@ -7,9 +7,12 @@ import axios from 'axios' | ||
import permissions from './src/permissions.js' | ||
import screenDetails from './src/screen.js' | ||
import screen from './src/screen.js' | ||
import browser from './src/browser.js' | ||
import locales from './src/locales.js' | ||
import intl from './src/intl.js' | ||
import audio from './src/audio.js' | ||
import webgl from './src/webgl.js' | ||
import mathDetails from './src/math.js' | ||
import math from './src/math.js' | ||
import fonts from './src/fonts.js' | ||
import storage from './src/storage.js' | ||
import devices from './src/devices.js' | ||
import webrtc from './src/webrtc.js' | ||
@@ -19,28 +22,15 @@ export default async function stealth({ apiKey }) { | ||
return Promise.all([ | ||
{ canvas: await canvas() }, | ||
{ device: device() }, | ||
{ | ||
permissions: await permissions() | ||
}, | ||
{ | ||
screen: await screenDetails() | ||
}, | ||
{ | ||
browser: await browser() | ||
}, | ||
{ | ||
locales: await locales() | ||
}, | ||
{ | ||
audio: await audio() | ||
}, | ||
{ | ||
webgl: await webgl() | ||
}, | ||
{ | ||
math: await mathDetails() | ||
}, | ||
{ | ||
fonts: await fonts() | ||
} | ||
audio(), | ||
browser(), | ||
canvas(), | ||
device(), | ||
devices(), | ||
fonts(), | ||
intl(), | ||
math(), | ||
permissions(), | ||
screen(), | ||
storage(), | ||
webgl(), | ||
webrtc() | ||
]).then((data) => { | ||
@@ -47,0 +37,0 @@ const local = data.reduce((acc, cur) => { |
{ | ||
"name": "@stltio/stealth", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "index", |
@@ -7,2 +7,4 @@ # Stlt - Stealth | ||
See the working [demo](https://stlt.io) in action. | ||
## How to use | ||
@@ -36,1 +38,20 @@ | ||
``` | ||
## ApiKey | ||
If `apiKey` is provided, send the payload to the server (more accurate results). | ||
Want an API_KEY? Contact us at [hello@stlt.io](mailto:hello@stlt.io). | ||
### Example with API_KEY | ||
``` | ||
import stealth from '@stltio/stealth' | ||
const result = await stealth({ apiKey: 'aaa...bbb'}) | ||
const { | ||
local: {}, | ||
remote: {}, | ||
visitorId: 'abc...xyz', | ||
ms: 491 | ||
} = result | ||
``` |
import hash from './hash.js' | ||
const audio = () => { | ||
return new Promise((resolve, reject) => { | ||
return new Promise((resolve) => { | ||
try { | ||
// Set up audio parameters | ||
const sampleRate = 44100 | ||
@@ -29,6 +28,8 @@ const numSamples = 5000 | ||
resolve({ | ||
sampleHash: hash(samples.join(',')), | ||
oscillator: oscillator.type, | ||
maxChannels: audioContext.destination.maxChannelCount, | ||
channelCountMode: audioBuffer.channelCountMode | ||
audio: { | ||
sampleHash: hash(samples.join(',')), | ||
oscillator: oscillator.type, | ||
maxChannels: audioContext.destination.maxChannelCount, | ||
channelCountMode: audioBuffer.channelCountMode | ||
} | ||
}) | ||
@@ -39,4 +40,3 @@ } | ||
} catch (error) { | ||
console.error('Error creating audio fingerprint:', error) | ||
reject(error) | ||
resolve({ audio: null }) | ||
} | ||
@@ -43,0 +43,0 @@ }) |
const browser = () => { | ||
return new Promise((resolve, reject) => { | ||
return new Promise((resolve) => { | ||
try { | ||
resolve({ | ||
userAgent: navigator.userAgent, | ||
cookieEnabled: navigator.cookieEnabled, | ||
onLine: navigator.onLine, | ||
oscpu: navigator.oscpu, | ||
userAgentData: navigator.userAgentData, | ||
webdriver: navigator.webdriver, | ||
doNotTrack: navigator.doNotTrack, | ||
pdfViewerEnabled: navigator.pdfViewerEnabled, | ||
applePayVersion: getApplePayVersion(), | ||
...getBrowser() | ||
browser: { | ||
userAgent: navigator.userAgent, | ||
cookieEnabled: navigator.cookieEnabled, | ||
onLine: navigator.onLine, | ||
oscpu: navigator.oscpu, | ||
userAgentData: navigator.userAgentData, | ||
webdriver: navigator.webdriver, | ||
doNotTrack: navigator.doNotTrack, | ||
pdfViewerEnabled: navigator.pdfViewerEnabled, | ||
applePayVersion: getApplePayVersion(), | ||
...getBrowser() | ||
} | ||
}) | ||
} catch (error) { | ||
reject(error) | ||
resolve({ browser: null }) | ||
} | ||
@@ -19,0 +21,0 @@ }) |
import hash from './hash.js' | ||
const canvas = () => { | ||
return new Promise((resolve, reject) => { | ||
return new Promise((resolve) => { | ||
try { | ||
@@ -19,5 +19,5 @@ const canvas = document.createElement('canvas') | ||
resolve(hash(canvas.toDataURL())) | ||
resolve({ canvas: hash(canvas.toDataURL()) }) | ||
} catch (error) { | ||
reject(error) | ||
resolve({ canvas: null }) | ||
} | ||
@@ -24,0 +24,0 @@ }) |
const device = () => { | ||
const memoryInfo = | ||
window.performance && window.performance.memory | ||
? window.performance.memory | ||
: 0 | ||
return new Promise((resolve) => { | ||
try { | ||
const jsHeapSizeLimit = | ||
window.performance && window.performance.memory | ||
? window.performance.memory.jsHeapSizeLimit | ||
: 0 | ||
return { | ||
hardwareConcurrency: navigator.hardwareConcurrency, | ||
memory: navigator.deviceMemory, | ||
platform: navigator?.userAgentData?.platform || 'unknown', | ||
mobile: navigator?.userAgentData?.mobile || 'unknown', | ||
vendor: navigator?.userAgentData?.vendor || 'unknown', | ||
architecture: getArchitecture(), | ||
videoCard: getVideoCard() | ||
} | ||
resolve({ | ||
device: { | ||
jsHeapSizeLimit, | ||
hardwareConcurrency: navigator.hardwareConcurrency, | ||
memory: navigator.deviceMemory, | ||
platform: navigator?.userAgentData?.platform || 'unknown', | ||
mobile: navigator?.userAgentData?.mobile || 'unknown', | ||
vendor: navigator?.userAgentData?.vendor || 'unknown', | ||
architecture: getArchitecture(), | ||
videoCard: getVideoCard() | ||
} | ||
}) | ||
} catch { | ||
resolve({ device: null }) | ||
} | ||
}) | ||
} | ||
@@ -17,0 +26,0 @@ |
@@ -1,24 +0,26 @@ | ||
const mathDetails = () => { | ||
return new Promise((resolve, reject) => { | ||
const math = () => { | ||
return new Promise((resolve) => { | ||
try { | ||
resolve({ | ||
acos: Math.acos(0.5), | ||
asin: integrate(Math.asin, -1, 1, 97), | ||
atan: integrate(Math.atan, -1, 1, 97), | ||
cos: integrate(Math.cos, 0, Math.PI, 97), | ||
cosh: Math.cosh(9 / 7), | ||
e: Math.E, | ||
largeCos: Math.cos(1e20), | ||
largeSin: Math.sin(1e20), | ||
largeTan: Math.tan(1e20), | ||
log: Math.log(1000), | ||
pi: Math.PI, | ||
sin: integrate(Math.sin, -Math.PI, Math.PI, 97), | ||
sinh: integrate(Math.sinh, -9 / 7, 7 / 9, 97), | ||
sqrt: Math.sqrt(2), | ||
tan: integrate(Math.tan, 0, 2 * Math.PI, 97), | ||
tanh: integrate(Math.tanh, -9 / 7, 7 / 9, 97) | ||
math: { | ||
acos: Math.acos(0.5), | ||
asin: integrate(Math.asin, -1, 1, 97), | ||
atan: integrate(Math.atan, -1, 1, 97), | ||
cos: integrate(Math.cos, 0, Math.PI, 97), | ||
cosh: Math.cosh(9 / 7), | ||
e: Math.E, | ||
largeCos: Math.cos(1e20), | ||
largeSin: Math.sin(1e20), | ||
largeTan: Math.tan(1e20), | ||
log: Math.log(1000), | ||
pi: Math.PI, | ||
sin: integrate(Math.sin, -Math.PI, Math.PI, 97), | ||
sinh: integrate(Math.sinh, -9 / 7, 7 / 9, 97), | ||
sqrt: Math.sqrt(2), | ||
tan: integrate(Math.tan, 0, 2 * Math.PI, 97), | ||
tanh: integrate(Math.tanh, -9 / 7, 7 / 9, 97) | ||
} | ||
}) | ||
} catch (error) { | ||
reject(error) | ||
} catch { | ||
resolve({ math: null }) | ||
} | ||
@@ -37,2 +39,2 @@ }) | ||
export default mathDetails | ||
export default math |
const permissions = async () => { | ||
const getPermissionState = (name) => | ||
navigator.permissions | ||
.query({ name }) | ||
.then((res) => ({ name, state: res.state })) | ||
.catch((error) => ({ name, state: 'unknown' })) | ||
return !('permissions' in navigator) | ||
? undefined | ||
: Promise.all([ | ||
return new Promise((resolve) => { | ||
try { | ||
Promise.all([ | ||
getPermissionState('accelerometer'), | ||
@@ -35,15 +29,27 @@ getPermissionState('ambient-light-sensor'), | ||
.then((permissions) => { | ||
return permissions.reduce((acc, perm) => { | ||
const { state, name } = perm || {} | ||
if (acc[state]) { | ||
acc[state].push(name) | ||
resolve({ | ||
permissions: permissions.reduce((acc, perm) => { | ||
const { state, name } = perm || {} | ||
if (acc[state]) { | ||
acc[state].push(name) | ||
return acc | ||
} | ||
acc[state] = [name] | ||
return acc | ||
} | ||
acc[state] = [name] | ||
return acc | ||
}, {}) | ||
}, {}) | ||
}) | ||
}) | ||
.catch((error) => console.error(error)) | ||
.catch(() => resolve({ permissions: null })) | ||
} catch { | ||
resolve({ permissions: null }) | ||
} | ||
}) | ||
} | ||
const getPermissionState = (name) => | ||
navigator.permissions | ||
.query({ name }) | ||
.then((res) => ({ name, state: res.state })) | ||
.catch(() => ({ name, state: 'unknown' })) | ||
export default permissions |
const screenDetails = () => { | ||
return new Promise((resolve, reject) => { | ||
return new Promise((resolve) => { | ||
try { | ||
resolve({ | ||
isTouchscreen: navigator.maxTouchPoints > 0, | ||
maxTouchPoints: navigator.maxTouchPoints, | ||
colorDepth: screen.colorDepth, | ||
mediaMatches: matchMedias() | ||
screen: { | ||
isTouchscreen: navigator.maxTouchPoints > 0, | ||
maxTouchPoints: navigator.maxTouchPoints, | ||
colorDepth: screen.colorDepth, | ||
mediaMatches: matchMedias() | ||
} | ||
}) | ||
} catch (error) { | ||
reject(error) | ||
resolve({ screen: null }) | ||
} | ||
@@ -13,0 +15,0 @@ }) |
import hash from './hash.js' | ||
const webgl = async () => { | ||
const canvas = document.createElement('canvas') | ||
const width = 256 | ||
const height = 128 | ||
return new Promise((resolve) => { | ||
try { | ||
const canvas = document.createElement('canvas') | ||
const width = 256 | ||
const height = 128 | ||
const ctx = | ||
canvas.getContext('webgl2') || | ||
canvas.getContext('experimental-webgl2') || | ||
canvas.getContext('webgl') || | ||
canvas.getContext('experimental-webgl') || | ||
canvas.getContext('moz-webgl') | ||
const ctx = | ||
canvas.getContext('webgl2') || | ||
canvas.getContext('experimental-webgl2') || | ||
canvas.getContext('webgl') || | ||
canvas.getContext('experimental-webgl') || | ||
canvas.getContext('moz-webgl') | ||
try { | ||
var f = | ||
'attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}' | ||
var g = | ||
'precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}' | ||
var h = ctx.createBuffer() | ||
var f = | ||
'attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}' | ||
var g = | ||
'precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}' | ||
var h = ctx.createBuffer() | ||
ctx.bindBuffer(ctx.ARRAY_BUFFER, h) | ||
ctx.bindBuffer(ctx.ARRAY_BUFFER, h) | ||
var i = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.7321, 0]) | ||
var i = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.7321, 0]) | ||
ctx.bufferData(ctx.ARRAY_BUFFER, i, ctx.STATIC_DRAW), | ||
(h.itemSize = 3), | ||
(h.numItems = 3) | ||
ctx.bufferData(ctx.ARRAY_BUFFER, i, ctx.STATIC_DRAW), | ||
(h.itemSize = 3), | ||
(h.numItems = 3) | ||
var j = ctx.createProgram() | ||
var k = ctx.createShader(ctx.VERTEX_SHADER) | ||
var j = ctx.createProgram() | ||
var k = ctx.createShader(ctx.VERTEX_SHADER) | ||
ctx.shaderSource(k, f) | ||
ctx.compileShader(k) | ||
ctx.shaderSource(k, f) | ||
ctx.compileShader(k) | ||
var l = ctx.createShader(ctx.FRAGMENT_SHADER) | ||
var l = ctx.createShader(ctx.FRAGMENT_SHADER) | ||
ctx.shaderSource(l, g) | ||
ctx.compileShader(l) | ||
ctx.attachShader(j, k) | ||
ctx.attachShader(j, l) | ||
ctx.linkProgram(j) | ||
ctx.useProgram(j) | ||
ctx.shaderSource(l, g) | ||
ctx.compileShader(l) | ||
ctx.attachShader(j, k) | ||
ctx.attachShader(j, l) | ||
ctx.linkProgram(j) | ||
ctx.useProgram(j) | ||
j.vertexPosAttrib = ctx.getAttribLocation(j, 'attrVertex') | ||
j.offsetUniform = ctx.getUniformLocation(j, 'uniformOffset') | ||
j.vertexPosAttrib = ctx.getAttribLocation(j, 'attrVertex') | ||
j.offsetUniform = ctx.getUniformLocation(j, 'uniformOffset') | ||
ctx.enableVertexAttribArray(j.vertexPosArray) | ||
ctx.vertexAttribPointer(j.vertexPosAttrib, h.itemSize, ctx.FLOAT, !1, 0, 0) | ||
ctx.uniform2f(j.offsetUniform, 1, 1) | ||
ctx.drawArrays(ctx.TRIANGLE_STRIP, 0, h.numItems) | ||
} catch (e) {} | ||
ctx.enableVertexAttribArray(j.vertexPosArray) | ||
ctx.vertexAttribPointer( | ||
j.vertexPosAttrib, | ||
h.itemSize, | ||
ctx.FLOAT, | ||
!1, | ||
0, | ||
0 | ||
) | ||
ctx.uniform2f(j.offsetUniform, 1, 1) | ||
ctx.drawArrays(ctx.TRIANGLE_STRIP, 0, h.numItems) | ||
var m = '' | ||
var n = new Uint8Array(width * height * 4) | ||
ctx.readPixels(0, 0, width, height, ctx.RGBA, ctx.UNSIGNED_BYTE, n) | ||
const m = JSON.stringify(n).replace(/,?"[0-9]+":/g, '') | ||
var n = new Uint8Array(width * height * 4) | ||
ctx.readPixels(0, 0, width, height, ctx.RGBA, ctx.UNSIGNED_BYTE, n) | ||
m = JSON.stringify(n).replace(/,?"[0-9]+":/g, '') | ||
return hash(m) | ||
resolve({ webgl: hash(m) }) | ||
} catch { | ||
resolve({ webgl: null }) | ||
} | ||
}) | ||
} | ||
export default webgl |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
94151
19
4262
56