318 lines
47 KiB
PHP
318 lines
47 KiB
PHP
|
|
<?php
|
|||
|
|
/** 二次风控加强判断:UAParser / Canvas / WebGL 深度 / WebGPU / 字体(仅 CLOAK_RISK_ENHANCED=ON 时 include) */
|
|||
|
|
?>
|
|||
|
|
<script>
|
|||
|
|
(function(window,undefined){"use strict";var LIBVERSION="2.0.5",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i<arr.length;i++){enums[arr[i].toUpperCase()]=arr[i]}return enums},has=function(str1,str2){if(typeof str1===OBJ_TYPE&&str1.length>0){for(var i in str1){if(lowerize(str2)==lowerize(str1[i]))return true}return false}return isString(str1)?lowerize(str2)==lowerize(str1):false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i<tokens.length;i++){if(tokens[i].indexOf(";")>-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){if(!arr.hasOwnProperty(i))continue;var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i<arrays.length&&!matches){var regex=arrays[i],props=arrays[i+1];j=k=0;while(j<regex.length&&!matches){if(!regex[j]){break}matches=regex[j++].exec(ua);if(!!matches){for(p=0;p<props.length;p++){match=matches[++k];q=props[p];if(typeof q===OBJ_TYPE&&q.length>0){if(q.length===2){if
|
|||
|
|
</script>
|
|||
|
|
<script>
|
|||
|
|
async function getWebGPUFingerprint() {
|
|||
|
|
const fingerprint = {
|
|||
|
|
supported: false,
|
|||
|
|
adapterInfo: null,
|
|||
|
|
limits: null,
|
|||
|
|
benchmark: {
|
|||
|
|
durationMs: -1
|
|||
|
|
},
|
|||
|
|
error: null
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
if (!navigator.gpu) {
|
|||
|
|
fingerprint.error = 'WebGPU API (navigator.gpu) not found.';
|
|||
|
|
return fingerprint;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fingerprint.supported = true;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const adapter = await navigator.gpu.requestAdapter();
|
|||
|
|
if (!adapter) {
|
|||
|
|
fingerprint.error = 'Failed to get GPU adapter.';
|
|||
|
|
return fingerprint;
|
|||
|
|
}
|
|||
|
|
if (adapter.requestAdapterInfo) {
|
|||
|
|
fingerprint.adapterInfo = await adapter.requestAdapterInfo();
|
|||
|
|
} else {
|
|||
|
|
fingerprint.adapterInfo = { unavailable: true };
|
|||
|
|
}
|
|||
|
|
fingerprint.limits = adapter.limits;
|
|||
|
|
const device = await adapter.requestDevice();
|
|||
|
|
const shaderCode = `
|
|||
|
|
@group(0) @binding(0) var<storage, read_write> data: array<f32>;
|
|||
|
|
@compute @workgroup_size(1)
|
|||
|
|
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
|
|||
|
|
let index = id.x;
|
|||
|
|
data[index] = data[index] * f32(index) / 1000.0;
|
|||
|
|
}
|
|||
|
|
`;
|
|||
|
|
const shaderModule = device.createShaderModule({ code: shaderCode });
|
|||
|
|
const pipeline = device.createComputePipeline({
|
|||
|
|
layout: 'auto',
|
|||
|
|
compute: { module: shaderModule, entryPoint: 'main' }
|
|||
|
|
});
|
|||
|
|
const input = new Float32Array(1024 * 1024); // 1M floats
|
|||
|
|
const buffer = device.createBuffer({
|
|||
|
|
size: input.byteLength,
|
|||
|
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
|
|||
|
|
});
|
|||
|
|
device.queue.writeBuffer(buffer, 0, input);
|
|||
|
|
const bindGroup = device.createBindGroup({
|
|||
|
|
layout: pipeline.getBindGroupLayout(0),
|
|||
|
|
entries: [{ binding: 0, resource: { buffer } }]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const startTime = performance.now();
|
|||
|
|
const commandEncoder = device.createCommandEncoder();
|
|||
|
|
const passEncoder = commandEncoder.beginComputePass();
|
|||
|
|
passEncoder.setPipeline(pipeline);
|
|||
|
|
passEncoder.setBindGroup(0, bindGroup);
|
|||
|
|
passEncoder.dispatchWorkgroups(input.length);
|
|||
|
|
passEncoder.end();
|
|||
|
|
device.queue.submit([commandEncoder.finish()]);
|
|||
|
|
await device.queue.onSubmittedWorkDone();
|
|||
|
|
const endTime = performance.now();
|
|||
|
|
fingerprint.benchmark.durationMs = endTime - startTime;
|
|||
|
|
} catch (e) {
|
|||
|
|
fingerprint.error = e.message;
|
|||
|
|
}
|
|||
|
|
return fingerprint;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var isFontSupportedByFallback = (fontFamily) => {
|
|||
|
|
const baseFonts = ['monospace', 'sans-serif', 'serif'];
|
|||
|
|
const testString = "mmmmmmmmmmlli";
|
|||
|
|
const testFontSize = '72px';
|
|||
|
|
|
|||
|
|
const body = document.body;
|
|||
|
|
const span = document.createElement('span');
|
|||
|
|
span.style.fontSize = testFontSize;
|
|||
|
|
span.innerHTML = testString;
|
|||
|
|
|
|||
|
|
const defaultWidths = {};
|
|||
|
|
const defaultHeights = {};
|
|||
|
|
|
|||
|
|
for (const baseFont of baseFonts) {
|
|||
|
|
span.style.fontFamily = baseFont;
|
|||
|
|
body.appendChild(span);
|
|||
|
|
defaultWidths[baseFont] = span.offsetWidth;
|
|||
|
|
defaultHeights[baseFont] = span.offsetHeight;
|
|||
|
|
body.removeChild(span);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let detected = false;
|
|||
|
|
for (const baseFont of baseFonts) {
|
|||
|
|
span.style.fontFamily = `"${fontFamily}", ${baseFont}`;
|
|||
|
|
body.appendChild(span);
|
|||
|
|
const matched = (span.offsetWidth !== defaultWidths[baseFont] || span.offsetHeight !== defaultHeights[baseFont]);
|
|||
|
|
body.removeChild(span);
|
|||
|
|
if (matched) {
|
|||
|
|
detected = true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return detected;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const commonFonts = ['Arial', 'Verdana', 'Helvetica', 'Times New Roman', 'Courier New',
|
|||
|
|
'Calibri',
|
|||
|
|
'Segoe UI',
|
|||
|
|
'Helvetica Neue',
|
|||
|
|
'San Francisco'];
|
|||
|
|
|
|||
|
|
function cloakForEach(arr, fn) {
|
|||
|
|
if (Array.prototype.forEach && arr.forEach === Array.prototype.forEach) {
|
|||
|
|
arr.forEach(fn);
|
|||
|
|
} else if (arr.length === +arr.length) {
|
|||
|
|
for (let i = 0, len = arr.length; i < len; i++) {
|
|||
|
|
fn(arr[i], i, arr);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
for (let k in arr) {
|
|||
|
|
if (arr.hasOwnProperty(k)) {
|
|||
|
|
fn(arr[k], k, arr);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function cloakWebglLoseContext(gl) {
|
|||
|
|
const ext = gl.getExtension("WEBGL_lose_context");
|
|||
|
|
if (ext != null) {
|
|||
|
|
ext.loseContext();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function cloakCanvasFingerprint() {
|
|||
|
|
let out = [];
|
|||
|
|
let canvas = document.createElement("canvas");
|
|||
|
|
canvas.width = 2000;
|
|||
|
|
canvas.height = 200;
|
|||
|
|
canvas.style.display = "inline";
|
|||
|
|
let ctx = canvas.getContext("2d");
|
|||
|
|
ctx.rect(0, 0, 10, 10);
|
|||
|
|
ctx.rect(2, 2, 6, 6);
|
|||
|
|
out.push("canvas winding:" + (ctx.isPointInPath(5, 5, "evenodd") === false ? "yes" : "no"));
|
|||
|
|
ctx.textBaseline = "alphabetic";
|
|||
|
|
ctx.fillStyle = "#f60";
|
|||
|
|
ctx.fillRect(125, 1, 62, 20);
|
|||
|
|
ctx.fillStyle = "#069";
|
|||
|
|
ctx.font = "11pt Arial";
|
|||
|
|
ctx.fillText("Cwm fjordbank glyphs vext quiz, 😃", 2, 15);
|
|||
|
|
ctx.fillStyle = "rgba(102, 204, 0, 0.2)";
|
|||
|
|
ctx.font = "18pt Arial";
|
|||
|
|
ctx.fillText("Cwm fjordbank glyphs vext quiz, 😃", 4, 45);
|
|||
|
|
ctx.globalCompositeOperation = "multiply";
|
|||
|
|
ctx.fillStyle = "rgb(255,0,255)";
|
|||
|
|
ctx.beginPath();
|
|||
|
|
ctx.arc(50, 50, 50, 0, Math.PI * 2, true);
|
|||
|
|
ctx.closePath();
|
|||
|
|
ctx.fill();
|
|||
|
|
ctx.fillStyle = "rgb(0,255,255)";
|
|||
|
|
ctx.beginPath();
|
|||
|
|
ctx.arc(100, 50, 50, 0, Math.PI * 2, true);
|
|||
|
|
ctx.closePath();
|
|||
|
|
ctx.fill();
|
|||
|
|
ctx.fillStyle = "rgb(255,255,0)";
|
|||
|
|
ctx.beginPath();
|
|||
|
|
ctx.arc(75, 100, 50, 0, Math.PI * 2, true);
|
|||
|
|
ctx.closePath();
|
|||
|
|
ctx.fill();
|
|||
|
|
ctx.fillStyle = "rgb(255,0,255)";
|
|||
|
|
ctx.arc(75, 75, 75, 0, Math.PI * 2, true);
|
|||
|
|
ctx.arc(75, 75, 25, 0, Math.PI * 2, true);
|
|||
|
|
ctx.fill("evenodd");
|
|||
|
|
if (canvas.toDataURL) {
|
|||
|
|
out.push("canvas fp:" + canvas.toDataURL());
|
|||
|
|
}
|
|||
|
|
return out;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function cloakWebglDeepFingerprint(gl) {
|
|||
|
|
let fmtPair = function (pair) {
|
|||
|
|
gl.clearColor(0, 0, 0, 1);
|
|||
|
|
gl.enable(gl.DEPTH_TEST);
|
|||
|
|
gl.depthFunc(gl.LEQUAL);
|
|||
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|||
|
|
return "[" + pair[0] + ", " + pair[1] + "]";
|
|||
|
|
};
|
|||
|
|
let maxAniso = function (ctx) {
|
|||
|
|
let ext = ctx.getExtension("EXT_texture_filter_anisotropic") || ctx.getExtension("WEBKIT_EXT_texture_filter_anisotropic") || ctx.getExtension("MOZ_EXT_texture_filter_anisotropic");
|
|||
|
|
if (ext) {
|
|||
|
|
let v = ctx.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
|
|||
|
|
return v === 0 ? 2 : v;
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
};
|
|||
|
|
if (!gl) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
let lines = [];
|
|||
|
|
let vs = "attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}";
|
|||
|
|
let fs = "precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}";
|
|||
|
|
let buf = gl.createBuffer();
|
|||
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
|||
|
|
let verts = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.732134444, 0]);
|
|||
|
|
gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW);
|
|||
|
|
buf.itemSize = 3;
|
|||
|
|
buf.numItems = 3;
|
|||
|
|
let prog = gl.createProgram();
|
|||
|
|
let vsh = gl.createShader(gl.VERTEX_SHADER);
|
|||
|
|
gl.shaderSource(vsh, vs);
|
|||
|
|
gl.compileShader(vsh);
|
|||
|
|
let fsh = gl.createShader(gl.FRAGMENT_SHADER);
|
|||
|
|
gl.shaderSource(fsh, fs);
|
|||
|
|
gl.compileShader(fsh);
|
|||
|
|
gl.attachShader(prog, vsh);
|
|||
|
|
gl.attachShader(prog, fsh);
|
|||
|
|
gl.linkProgram(prog);
|
|||
|
|
gl.useProgram(prog);
|
|||
|
|
prog.vertexPosAttrib = gl.getAttribLocation(prog, "attrVertex");
|
|||
|
|
prog.offsetUniform = gl.getUniformLocation(prog, "uniformOffset");
|
|||
|
|
gl.enableVertexAttribArray(prog.vertexPosArray);
|
|||
|
|
gl.vertexAttribPointer(prog.vertexPosAttrib, buf.itemSize, gl.FLOAT, false, 0, 0);
|
|||
|
|
gl.uniform2f(prog.offsetUniform, 1, 1);
|
|||
|
|
gl.drawArrays(gl.TRIANGLE_STRIP, 0, buf.numItems);
|
|||
|
|
try {
|
|||
|
|
lines.push(gl.canvas.toDataURL());
|
|||
|
|
} catch (e) {}
|
|||
|
|
lines.push("extensions:" + (gl.getSupportedExtensions() || []).join(";"));
|
|||
|
|
lines.push("webgl aliased line width range:" + fmtPair(gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE)));
|
|||
|
|
lines.push("webgl aliased point size range:" + fmtPair(gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE)));
|
|||
|
|
lines.push("webgl alpha bits:" + gl.getParameter(gl.ALPHA_BITS));
|
|||
|
|
lines.push("webgl antialiasing:" + (gl.getContextAttributes().antialias ? "yes" : "no"));
|
|||
|
|
lines.push("webgl blue bits:" + gl.getParameter(gl.BLUE_BITS));
|
|||
|
|
lines.push("webgl depth bits:" + gl.getParameter(gl.DEPTH_BITS));
|
|||
|
|
lines.push("webgl green bits:" + gl.getParameter(gl.GREEN_BITS));
|
|||
|
|
lines.push("webgl max anisotropy:" + maxAniso(gl));
|
|||
|
|
lines.push("webgl max combined texture image units:" + gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS));
|
|||
|
|
lines.push("webgl max cube map texture size:" + gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE));
|
|||
|
|
lines.push("webgl max fragment uniform vectors:" + gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS));
|
|||
|
|
lines.push("webgl max render buffer size:" + gl.getParameter(gl.MAX_RENDERBUFFER_SIZE));
|
|||
|
|
lines.push("webgl max texture image units:" + gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS));
|
|||
|
|
lines.push("webgl max texture size:" + gl.getParameter(gl.MAX_TEXTURE_SIZE));
|
|||
|
|
lines.push("webgl max varying vectors:" + gl.getParameter(gl.MAX_VARYING_VECTORS));
|
|||
|
|
lines.push("webgl max vertex attribs:" + gl.getParameter(gl.MAX_VERTEX_ATTRIBS));
|
|||
|
|
lines.push("webgl max vertex texture image units:" + gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS));
|
|||
|
|
lines.push("webgl max vertex uniform vectors:" + gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS));
|
|||
|
|
lines.push("webgl max viewport dims:" + fmtPair(gl.getParameter(gl.MAX_VIEWPORT_DIMS)));
|
|||
|
|
lines.push("webgl red bits:" + gl.getParameter(gl.RED_BITS));
|
|||
|
|
lines.push("webgl renderer:" + gl.getParameter(gl.RENDERER));
|
|||
|
|
lines.push("webgl shading language version:" + gl.getParameter(gl.SHADING_LANGUAGE_VERSION));
|
|||
|
|
lines.push("webgl stencil bits:" + gl.getParameter(gl.STENCIL_BITS));
|
|||
|
|
lines.push("webgl vendor:" + gl.getParameter(gl.VENDOR));
|
|||
|
|
lines.push("webgl version:" + gl.getParameter(gl.VERSION));
|
|||
|
|
try {
|
|||
|
|
let dbg = gl.getExtension("WEBGL_debug_renderer_info");
|
|||
|
|
if (dbg) {
|
|||
|
|
lines.push("webgl unmasked vendor:" + gl.getParameter(dbg.UNMASKED_VENDOR_WEBGL));
|
|||
|
|
lines.push("webgl unmasked renderer:" + gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL));
|
|||
|
|
}
|
|||
|
|
} catch (e) {}
|
|||
|
|
if (!gl.getShaderPrecisionFormat) {
|
|||
|
|
cloakWebglLoseContext(gl);
|
|||
|
|
return lines;
|
|||
|
|
}
|
|||
|
|
cloakForEach(["FLOAT", "INT"], function (typeName) {
|
|||
|
|
cloakForEach(["VERTEX", "FRAGMENT"], function (shaderKind) {
|
|||
|
|
cloakForEach(["HIGH", "MEDIUM", "LOW"], function (prec) {
|
|||
|
|
cloakForEach(["precision", "rangeMin", "rangeMax"], function (field) {
|
|||
|
|
let val = gl.getShaderPrecisionFormat(gl[shaderKind + "_SHADER"], gl[prec + "_" + typeName])[field];
|
|||
|
|
let label = field !== "precision" ? "precision " + field : field;
|
|||
|
|
lines.push(["webgl ", shaderKind.toLowerCase(), " shader ", prec.toLowerCase(), " ", typeName.toLowerCase(), " ", label, ":", val].join(""));
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
cloakWebglLoseContext(gl);
|
|||
|
|
return lines;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function cloakApplyEnhancedFingerprint(dataMap) {
|
|||
|
|
dataMap.set("canvas", cloakCanvasFingerprint());
|
|||
|
|
let canvas = document.createElement("canvas");
|
|||
|
|
let gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
|||
|
|
if (gl !== null) {
|
|||
|
|
let dbgExt = gl.getExtension("WEBGL_debug_renderer_info");
|
|||
|
|
let vendor;
|
|||
|
|
if (dbgExt && typeof gl.getParameter(dbgExt.UNMASKED_VENDOR_WEBGL) !== "undefined") {
|
|||
|
|
vendor = gl.getParameter(dbgExt.UNMASKED_VENDOR_WEBGL);
|
|||
|
|
} else {
|
|||
|
|
vendor = gl.getParameter(gl.RENDERER);
|
|||
|
|
}
|
|||
|
|
let renderer;
|
|||
|
|
if (dbgExt && typeof gl.getParameter(dbgExt.UNMASKED_RENDERER_WEBGL) !== "undefined") {
|
|||
|
|
renderer = gl.getParameter(dbgExt.UNMASKED_RENDERER_WEBGL);
|
|||
|
|
} else {
|
|||
|
|
renderer = gl.getParameter(gl.RENDERER);
|
|||
|
|
}
|
|||
|
|
dataMap.set("vendor", vendor).set("renderer", renderer).set("webgl", cloakWebglDeepFingerprint(gl));
|
|||
|
|
}
|
|||
|
|
const webGPUData = await getWebGPUFingerprint();
|
|||
|
|
dataMap.set("webGpuData", webGPUData);
|
|||
|
|
const fonts = commonFonts.filter(function (font) { return isFontSupportedByFallback(font); });
|
|||
|
|
dataMap.set("fonts", fonts);
|
|||
|
|
const uapResult = new UAParser();
|
|||
|
|
uapResult.setUA(dataMap.get("userAgent"));
|
|||
|
|
dataMap.set("browser", uapResult.getResult());
|
|||
|
|
}
|
|||
|
|
</script>
|