mirror of
https://github.com/kkapsner/CanvasBlocker
synced 2024-12-22 12:50:36 +01:00
test updates
This commit is contained in:
parent
717e1d3e3a
commit
0d0e3e30ec
@ -53,6 +53,7 @@
|
||||
"no-useless-rename": "warn",
|
||||
"no-var": "error",
|
||||
"quotes": ["error", "double"],
|
||||
"require-atomic-updates": "off",
|
||||
"semi": ["error", "always"],
|
||||
"space-in-parens": ["error", "never"],
|
||||
"strict": ["error", "function"],
|
||||
@ -68,7 +69,6 @@
|
||||
{
|
||||
"files": ["test/*"],
|
||||
"rules": {
|
||||
"no-var": "off",
|
||||
"no-console": "off"
|
||||
}
|
||||
},
|
||||
|
@ -2,7 +2,7 @@
|
||||
"use strict";
|
||||
|
||||
function byteArrayToHex(arrayBuffer){
|
||||
var chunks = [];
|
||||
const chunks = [];
|
||||
(new Uint32Array(arrayBuffer)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
@ -11,17 +11,17 @@
|
||||
}).join("");
|
||||
}
|
||||
|
||||
var container = document.getElementById("test");
|
||||
var hashContainer = container.querySelector(".hashes");
|
||||
var hashSets = Object.create(null);
|
||||
const container = document.getElementById("test");
|
||||
const hashContainer = container.querySelector(".hashes");
|
||||
let hashSets = Object.create(null);
|
||||
|
||||
function createSet(set){
|
||||
if (!hashSets[set]){
|
||||
var setContainer = document.createElement("tbody");
|
||||
const setContainer = document.createElement("tbody");
|
||||
hashContainer.appendChild(setContainer);
|
||||
var nameRow = document.createElement("tr");
|
||||
const nameRow = document.createElement("tr");
|
||||
setContainer.appendChild(nameRow);
|
||||
var nameContainer = document.createElement("th");
|
||||
const nameContainer = document.createElement("th");
|
||||
nameRow.appendChild(nameContainer);
|
||||
nameContainer.colSpan = 2;
|
||||
nameContainer.textContent = set;
|
||||
@ -29,37 +29,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
function displayData(data, set, title){
|
||||
async function displayData(data, set, title){
|
||||
createSet(set);
|
||||
var container = document.createElement("tr");
|
||||
const container = document.createElement("tr");
|
||||
|
||||
var titleNode = document.createElement("td");
|
||||
const titleNode = document.createElement("td");
|
||||
titleNode.textContent = title;
|
||||
container.appendChild(titleNode);
|
||||
|
||||
var hashNode = document.createElement("td");
|
||||
const hashNode = document.createElement("td");
|
||||
hashNode.textContent = "calculating hash";
|
||||
container.appendChild(hashNode);
|
||||
|
||||
crypto.subtle.digest("SHA-256", data).then(function(hash){
|
||||
hashNode.textContent = byteArrayToHex(hash);
|
||||
return;
|
||||
}).catch(function(error){
|
||||
hashNode.textContent = error;
|
||||
});
|
||||
hashSets[set].appendChild(container);
|
||||
|
||||
const hash = await crypto.subtle.digest("SHA-256", data);
|
||||
hashNode.textContent = byteArrayToHex(hash);
|
||||
}
|
||||
|
||||
function getAudioContext(frequency = 1e4){
|
||||
var context = new window.OfflineAudioContext(2, 44100, 44100);
|
||||
const context = new window.OfflineAudioContext(2, 44100, 44100);
|
||||
|
||||
// Create oscillator
|
||||
var pxi_oscillator = context.createOscillator();
|
||||
const pxi_oscillator = context.createOscillator();
|
||||
pxi_oscillator.type = "triangle";
|
||||
pxi_oscillator.frequency.value = frequency;
|
||||
|
||||
// Create and configure compressor
|
||||
var pxi_compressor = context.createDynamicsCompressor();
|
||||
const pxi_compressor = context.createDynamicsCompressor();
|
||||
pxi_compressor.threshold && (pxi_compressor.threshold.value = -50);
|
||||
pxi_compressor.knee && (pxi_compressor.knee.value = 40);
|
||||
pxi_compressor.ratio && (pxi_compressor.ratio.value = 12);
|
||||
@ -77,28 +74,28 @@
|
||||
}
|
||||
|
||||
function createEmptyData(){
|
||||
var emptyArray = new Float32Array(44100);
|
||||
const emptyArray = new Float32Array(44100);
|
||||
displayData(emptyArray, "empty buffer", "no API involved");
|
||||
|
||||
var emptyContext = new OfflineAudioContext(1, 44100, 44100);
|
||||
var emptyBuffer = emptyContext.createBuffer(1, 44100, 44100);
|
||||
const emptyContext = new OfflineAudioContext(1, 44100, 44100);
|
||||
const emptyBuffer = emptyContext.createBuffer(1, 44100, 44100);
|
||||
|
||||
var emptyCopy = new Float32Array(44100);
|
||||
const emptyCopy = new Float32Array(44100);
|
||||
emptyBuffer.copyFromChannel(emptyCopy, 0);
|
||||
displayData(emptyCopy, "empty buffer", "copyFromChannel - first");
|
||||
|
||||
var emptyData = emptyBuffer.getChannelData(0);
|
||||
const emptyData = emptyBuffer.getChannelData(0);
|
||||
displayData(emptyData, "empty buffer", "getChannelData - first");
|
||||
displayData(emptyBuffer.getChannelData(0), "empty buffer", "getChannelData - second");
|
||||
|
||||
var emptyCopy2 = new Float32Array(44100);
|
||||
const emptyCopy2 = new Float32Array(44100);
|
||||
emptyBuffer.copyFromChannel(emptyCopy2, 0);
|
||||
displayData(emptyCopy2, "empty buffer", "copyFromChannel - second");
|
||||
}
|
||||
|
||||
function getIframeWindow(){
|
||||
var l = window.length;
|
||||
var iframe = document.createElement("iframe");
|
||||
const l = window.length;
|
||||
const iframe = document.createElement("iframe");
|
||||
document.body.appendChild(iframe);
|
||||
const iframeWindow = window[l];
|
||||
document.body.removeChild(iframe);
|
||||
@ -107,43 +104,43 @@
|
||||
|
||||
function createHashData(frequency = 1e4){
|
||||
|
||||
var context = getAudioContext(frequency);
|
||||
const context = getAudioContext(frequency);
|
||||
|
||||
var setName = " (" + frequency + " Hz)";
|
||||
const setName = " (" + frequency + " Hz)";
|
||||
createSet(setName);
|
||||
|
||||
// Start audio processing
|
||||
context.startRendering();
|
||||
context.oncomplete = function(event){
|
||||
var copyTestIframe = new (getIframeWindow().Float32Array)(44100);
|
||||
const copyTestIframe = new (getIframeWindow().Float32Array)(44100);
|
||||
getIframeWindow().AudioBuffer.prototype.copyFromChannel.call(event.renderedBuffer, copyTestIframe, 0);
|
||||
displayData(copyTestIframe, setName, "copyFromChannel - iframe");
|
||||
|
||||
var chunkTest = new Float32Array(44100);
|
||||
var number = new Float32Array(100);
|
||||
for (var chunkI = 0; chunkI < 44100; chunkI += number.length){
|
||||
const chunkTest = new Float32Array(44100);
|
||||
const number = new Float32Array(100);
|
||||
for (let chunkI = 0; chunkI < 44100; chunkI += number.length){
|
||||
event.renderedBuffer.copyFromChannel(number, 0, chunkI);
|
||||
chunkTest.set(number, chunkI);
|
||||
}
|
||||
displayData(chunkTest, setName, "copyFromChannel - chunks");
|
||||
|
||||
var copyTest = new Float32Array(44100);
|
||||
const copyTest = new Float32Array(44100);
|
||||
event.renderedBuffer.copyFromChannel(copyTest, 0);
|
||||
displayData(copyTest, setName, "copyFromChannel - first");
|
||||
|
||||
|
||||
var getTest = event.renderedBuffer.getChannelData(0);
|
||||
const getTest = event.renderedBuffer.getChannelData(0);
|
||||
displayData(getTest, setName, "getChannelData - first");
|
||||
displayData(event.renderedBuffer.getChannelData(0), setName, "getChannelData - second readout");
|
||||
displayData(event.renderedBuffer.getChannelData(1), setName, "getChannelData - second channel");
|
||||
|
||||
var copyTest2 = new Float32Array(44100);
|
||||
const copyTest2 = new Float32Array(44100);
|
||||
event.renderedBuffer.copyFromChannel(copyTest2, 0);
|
||||
displayData(copyTest2, setName, "copyFromChannel - second");
|
||||
|
||||
if (frequency === 1e4){
|
||||
var sum = 0;
|
||||
for (var i = 4500; i < 5000; i += 1) {
|
||||
let sum = 0;
|
||||
for (let i = 4500; i < 5000; i += 1) {
|
||||
sum += Math.abs(getTest[i]);
|
||||
}
|
||||
container.querySelector(".sum").textContent = sum;
|
||||
|
@ -5,9 +5,9 @@ function draw(canvas){
|
||||
canvas.setAttribute("width", 220);
|
||||
canvas.setAttribute("height", 30);
|
||||
|
||||
var fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
const fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.textBaseline = "top";
|
||||
ctx.font = "14px 'Arial'";
|
||||
ctx.textBaseline = "alphabetic";
|
||||
@ -24,33 +24,31 @@ function test(window){
|
||||
"use strict";
|
||||
|
||||
// create window canvas
|
||||
var canvas = document.createElement("canvas");
|
||||
const canvas = document.createElement("canvas");
|
||||
// draw image in window canvas
|
||||
draw(canvas);
|
||||
return window.HTMLCanvasElement.prototype.toDataURL.call(canvas);
|
||||
}
|
||||
function hash(string){
|
||||
async function hash(string){
|
||||
"use strict";
|
||||
|
||||
var buffer = new TextEncoder("utf-8").encode(string);
|
||||
return crypto.subtle.digest("SHA-256", buffer).then(function(hash){
|
||||
var chunks = [];
|
||||
(new Uint32Array(hash)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
return chunks.map(function(chunk){
|
||||
return "0".repeat(8 - chunk.length) + chunk;
|
||||
}).join("");
|
||||
const buffer = new TextEncoder("utf-8").encode(string);
|
||||
const hash = await crypto.subtle.digest("SHA-256", buffer);
|
||||
const chunks = [];
|
||||
(new Uint32Array(hash)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
|
||||
return chunks.map(function(chunk){
|
||||
return "0".repeat(8 - chunk.length) + chunk;
|
||||
}).join("");
|
||||
}
|
||||
|
||||
var addLine = function(){
|
||||
const addLine = function(){
|
||||
"use strict";
|
||||
|
||||
var output = document.getElementById("results");
|
||||
const output = document.getElementById("results");
|
||||
return function(text){
|
||||
var line = document.createElement("div");
|
||||
const line = document.createElement("div");
|
||||
line.textContent = text;
|
||||
output.appendChild(line);
|
||||
};
|
||||
@ -59,13 +57,9 @@ var addLine = function(){
|
||||
addLine("window name at start: " + window.name);
|
||||
window.name = "CanvasBlocker CSP test";
|
||||
addLine("window name after set: " + window.name);
|
||||
hash(test(window)).then(function(hash){
|
||||
(async function(){
|
||||
"use strict";
|
||||
|
||||
addLine("canvas hash: " + hash);
|
||||
return;
|
||||
}).catch(function(error){
|
||||
"use strict";
|
||||
|
||||
addLine("error while creating canvas hash: " + error);
|
||||
});
|
||||
const hashValue = await hash(test(window));
|
||||
addLine("canvas hash: " + hashValue);
|
||||
}());
|
@ -1,13 +1,13 @@
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
var iframeCode = atob(
|
||||
const iframeCode = atob(
|
||||
document.getElementById("iframe").src.replace(/^.+base64,/, "")
|
||||
);
|
||||
|
||||
document.getElementById("code").textContent = iframeCode;
|
||||
|
||||
var blob = new Blob([iframeCode], {type: "text/html"});
|
||||
var newurl = window.URL.createObjectURL(blob);
|
||||
const blob = new Blob([iframeCode], {type: "text/html"});
|
||||
const newurl = window.URL.createObjectURL(blob);
|
||||
document.getElementById("blobIframe").src = newurl;
|
||||
}());
|
@ -82,9 +82,9 @@
|
||||
canvas.setAttribute("width", 220);
|
||||
canvas.setAttribute("height", 30);
|
||||
|
||||
var fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
const fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.textBaseline = "top";
|
||||
ctx.font = "14px 'Arial'";
|
||||
ctx.textBaseline = "alphabetic";
|
||||
@ -101,9 +101,9 @@
|
||||
"use strict";
|
||||
|
||||
// create window canvas
|
||||
var canvas = document.createElement("canvas");
|
||||
const canvas = document.createElement("canvas");
|
||||
// draw image in window canvas
|
||||
var ctx = draw(canvas);
|
||||
const ctx = draw(canvas);
|
||||
return {
|
||||
imageData: ctx.getImageData(0, 0, canvas.width, canvas.height),
|
||||
url: canvas.toDataURL(),
|
||||
@ -125,7 +125,7 @@
|
||||
function hashToString(hash){
|
||||
"use strict";
|
||||
|
||||
var chunks = [];
|
||||
const chunks = [];
|
||||
(new Uint32Array(hash)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
@ -134,38 +134,34 @@
|
||||
}).join("");
|
||||
}
|
||||
|
||||
var send = function(){
|
||||
const send = function(){
|
||||
"use strict";
|
||||
return function send(form, {url, imageData, isPointInPath}){
|
||||
var buffer = new TextEncoder("utf-8").encode(url);
|
||||
Promise.all([
|
||||
return async function send(form, {url, imageData, isPointInPath}){
|
||||
const buffer = new TextEncoder("utf-8").encode(url);
|
||||
const hashes = await Promise.all([
|
||||
crypto.subtle.digest("SHA-256", buffer),
|
||||
crypto.subtle.digest("SHA-256", imageData.data)
|
||||
]).then(function(hashes){
|
||||
var data = JSON.stringify({
|
||||
urlHash: hashToString(hashes[0]),
|
||||
imageDataHash: hashToString(hashes[1]),
|
||||
isPointInPath
|
||||
}, null, "\t");
|
||||
form.fingerprint.value = data;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", form.action + "?main", true);
|
||||
xhr.onreadystatechange = function(){
|
||||
if (this.readyState === 4){
|
||||
const status = this.status;
|
||||
if (status === 200 || status === 304) {
|
||||
console.log("Sending xhr successful from main page:", data);
|
||||
}
|
||||
else {
|
||||
console.log("Sending xhr failed:", this);
|
||||
}
|
||||
]);
|
||||
const data = JSON.stringify({
|
||||
urlHash: hashToString(hashes[0]),
|
||||
imageDataHash: hashToString(hashes[1]),
|
||||
isPointInPath
|
||||
}, null, "\t");
|
||||
form.fingerprint.value = data;
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", form.action + "?main", true);
|
||||
xhr.onreadystatechange = function(){
|
||||
if (this.readyState === 4){
|
||||
const status = this.status;
|
||||
if (status === 200 || status === 304) {
|
||||
console.log("Sending xhr successful from main page:", data);
|
||||
}
|
||||
};
|
||||
xhr.send(new FormData(form));
|
||||
return;
|
||||
}).catch(function(error){
|
||||
console.error(error);
|
||||
});
|
||||
else {
|
||||
console.log("Sending xhr failed:", this);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send(new FormData(form));
|
||||
};
|
||||
}();
|
||||
|
||||
|
@ -1,19 +1,19 @@
|
||||
var addTest = (function(){
|
||||
const addTest = (function(){
|
||||
"use strict";
|
||||
|
||||
var statusDefinitions = [
|
||||
const statusDefinitions = [
|
||||
{className: "notRun", text: "not run"},
|
||||
{className: "loud", text: "API tampering detected"},
|
||||
{className: "stealthy", text: "no API tampering detected"},
|
||||
{className: "failed", text: "test failed"}
|
||||
];
|
||||
var ul = document.getElementById("tests");
|
||||
const ul = document.getElementById("tests");
|
||||
return function addTest(name, func){
|
||||
var logs = [];
|
||||
const logs = [];
|
||||
function log(){
|
||||
logs.push(Array.prototype.slice.call(arguments).join(" "));
|
||||
}
|
||||
var status = 0;
|
||||
let status = 0;
|
||||
try {
|
||||
status = func(log)? 1: 2;
|
||||
}
|
||||
@ -21,15 +21,15 @@ var addTest = (function(){
|
||||
console.log(error);
|
||||
status = 3;
|
||||
}
|
||||
var li = document.createElement("li");
|
||||
const li = document.createElement("li");
|
||||
li.className = statusDefinitions[status].className;
|
||||
var nameNode = document.createElement("span");
|
||||
const nameNode = document.createElement("span");
|
||||
nameNode.className = "name";
|
||||
nameNode.textContent = name;
|
||||
nameNode.title = func.toString();
|
||||
li.appendChild(nameNode);
|
||||
li.appendChild(document.createTextNode(": "));
|
||||
var statusNode = document.createElement("span");
|
||||
const statusNode = document.createElement("span");
|
||||
statusNode.className = "status";
|
||||
statusNode.textContent = statusDefinitions[status].text;
|
||||
statusNode.title = logs.join("\n");
|
||||
@ -41,15 +41,15 @@ var addTest = (function(){
|
||||
|
||||
function checkPropertyDescriptor(object, name, expectedDescriptor, log){
|
||||
"use strict";
|
||||
var descriptor = Object.getOwnPropertyDescriptor(object, name);
|
||||
var detected = false;
|
||||
const descriptor = Object.getOwnPropertyDescriptor(object, name);
|
||||
let detected = false;
|
||||
|
||||
function logProperty(desc, got, expected){
|
||||
log("Wrong", desc, "for", name, "- got:", got, "- expected: ", expected);
|
||||
}
|
||||
function compare(desc, getter){
|
||||
var got = getter(descriptor);
|
||||
var expected = getter(expectedDescriptor);
|
||||
const got = getter(descriptor);
|
||||
const expected = getter(expectedDescriptor);
|
||||
|
||||
if ((typeof expected) === "function"){
|
||||
if (got.name !== expected.name){
|
||||
@ -94,7 +94,7 @@ addTest("function length", function(log){
|
||||
});
|
||||
addTest("function code", function(log){
|
||||
"use strict";
|
||||
var codeDetected = false;
|
||||
let codeDetected = false;
|
||||
function checkFunctionCode(func, expectedName){
|
||||
log("checking", expectedName);
|
||||
if (!func.toString().match(
|
||||
@ -228,7 +228,6 @@ addTest("property descriptor", function(log){
|
||||
object: CanvasRenderingContext2D.prototype,
|
||||
name: "getImageData",
|
||||
descriptor: {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
value: function getImageData(x, y, w, h){},
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
@ -264,9 +263,9 @@ addTest("property descriptor", function(log){
|
||||
addTest("error provocation 1", function(log){
|
||||
"use strict";
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
var ctx = canvas.getContext("2d");
|
||||
var canvasBlocker = false;
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
let canvasBlocker = false;
|
||||
try{
|
||||
ctx.getImageData(0, 0, 0, 0);
|
||||
}
|
||||
@ -284,10 +283,10 @@ addTest("error provocation 1", function(log){
|
||||
addTest("error provocation 2", function(log){
|
||||
"use strict";
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = 0;
|
||||
var ctx = canvas.getContext("2d");
|
||||
var canvasBlocker = false;
|
||||
const ctx = canvas.getContext("2d");
|
||||
let canvasBlocker = false;
|
||||
try{
|
||||
ctx.getImageData(0, 0, 1, 1);
|
||||
log("no error provoked");
|
||||
@ -306,7 +305,7 @@ addTest("error provocation 2", function(log){
|
||||
addTest("error provocation 3", function(log){
|
||||
"use strict";
|
||||
|
||||
var canvasBlocker = false;
|
||||
let canvasBlocker = false;
|
||||
try{
|
||||
CanvasRenderingContext2D.prototype.getImageData.apply(undefined, [0, 0, 1, 1]);
|
||||
}
|
||||
@ -324,23 +323,23 @@ addTest("error provocation 3", function(log){
|
||||
addTest("error properties", function(log){
|
||||
"use strict";
|
||||
|
||||
var canvasBlocker = false;
|
||||
let canvasBlocker = false;
|
||||
try{
|
||||
CanvasRenderingContext2D.prototype.getImageData.apply(undefined, [0, 0, 1, 1]);
|
||||
}
|
||||
catch (error){
|
||||
try {
|
||||
var name = "TypeError";
|
||||
const name = "TypeError";
|
||||
if (error.name !== name && error instanceof TypeError){
|
||||
log("Error name wrong. Expected: ", name, "- got:", error.name);
|
||||
canvasBlocker = true;
|
||||
}
|
||||
var start = "@" + location.href.replace(/\.html$/, ".js");
|
||||
const start = "@" + location.href.replace(/\.html$/, ".js");
|
||||
if (!error.stack.startsWith(start)){
|
||||
log("Error stack starts wrong. Expected:", start, "- got :", error.stack.split(/\n/g, 2)[0]);
|
||||
canvasBlocker = true;
|
||||
}
|
||||
var message = "'getImageData' called on an object that " +
|
||||
const message = "'getImageData' called on an object that " +
|
||||
"does not implement interface CanvasRenderingContext2D.";
|
||||
if (error.message !== message){
|
||||
log("Error message wrong. Expected: ", message, "- got:", error.message);
|
||||
@ -356,12 +355,12 @@ addTest("error properties", function(log){
|
||||
function testKnownPixelValue(size, log){
|
||||
"use strict";
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.height = size;
|
||||
canvas.width = size;
|
||||
var context = canvas.getContext("2d");
|
||||
var imageData = new ImageData(canvas.width, canvas.height);
|
||||
var pixelValues = imageData.data;
|
||||
const context = canvas.getContext("2d");
|
||||
const imageData = new ImageData(canvas.width, canvas.height);
|
||||
const pixelValues = imageData.data;
|
||||
for (let i = 0; i < imageData.data.length; i += 1){
|
||||
if (i % 4 !== 3){
|
||||
pixelValues[i] = Math.floor(256 * Math.random());
|
||||
@ -371,8 +370,8 @@ function testKnownPixelValue(size, log){
|
||||
}
|
||||
}
|
||||
context.putImageData(imageData, 0, 0);
|
||||
var p = context.getImageData(0, 0, canvas.width, canvas.height).data;
|
||||
for (var i = 0; i < p.length; i += 1){
|
||||
const p = context.getImageData(0, 0, canvas.width, canvas.height).data;
|
||||
for (let i = 0; i < p.length; i += 1){
|
||||
if (p[i] !== pixelValues[i]){
|
||||
log("wrong value", p[i], "at", i, "expected", pixelValues[i]);
|
||||
return true;
|
||||
@ -393,9 +392,9 @@ addTest("known pixel value test 10", function(log){
|
||||
addTest("double readout test", function(log){
|
||||
"use strict";
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
var context = canvas.getContext("2d");
|
||||
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
for (let i = 0; i < imageData.data.length; i += 1){
|
||||
if (i % 4 !== 3){
|
||||
imageData.data[i] = Math.floor(256 * Math.random());
|
||||
@ -406,8 +405,8 @@ addTest("double readout test", function(log){
|
||||
}
|
||||
context.putImageData(imageData, 0, 0);
|
||||
|
||||
var imageData1 = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
var imageData2 = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const imageData1 = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const imageData2 = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
for (let i = 0; i < imageData2.data.length; i += 1){
|
||||
if (imageData1.data[i] !== imageData2.data[i]){
|
||||
log("mismatch at", i, ":",
|
||||
@ -424,9 +423,9 @@ addTest("double readout test", function(log){
|
||||
addTest("double readout test (toDataURL)", function(log){
|
||||
"use strict";
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
var context = canvas.getContext("2d");
|
||||
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
for (let i = 0; i < imageData.data.length; i += 1){
|
||||
if (i % 4 !== 3){
|
||||
imageData.data[i] = Math.floor(256 * Math.random());
|
||||
@ -437,8 +436,8 @@ addTest("double readout test (toDataURL)", function(log){
|
||||
}
|
||||
context.putImageData(imageData, 0, 0);
|
||||
|
||||
var dataURL1 = canvas.toDataURL();
|
||||
var dataURL2 = canvas.toDataURL();
|
||||
const dataURL1 = canvas.toDataURL();
|
||||
const dataURL2 = canvas.toDataURL();
|
||||
if (dataURL1 !== dataURL2){
|
||||
log("data URL missmatch:",
|
||||
dataURL1,
|
||||
@ -452,9 +451,9 @@ addTest("double readout test (toDataURL)", function(log){
|
||||
addTest("readout - in - out test", function(log){
|
||||
"use strict";
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
var context = canvas.getContext("2d");
|
||||
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
for (let i = 0; i < imageData.data.length; i += 1){
|
||||
if (i % 4 !== 3){
|
||||
imageData.data[i] = Math.floor(256 * Math.random());
|
||||
@ -465,11 +464,11 @@ addTest("readout - in - out test", function(log){
|
||||
}
|
||||
context.putImageData(imageData, 0, 0);
|
||||
|
||||
var imageData1 = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
var canvas2 = document.createElement("canvas");
|
||||
var context2 = canvas2.getContext("2d");
|
||||
const imageData1 = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const canvas2 = document.createElement("canvas");
|
||||
const context2 = canvas2.getContext("2d");
|
||||
context2.putImageData(imageData1, 0, 0);
|
||||
var imageData2 = context2.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const imageData2 = context2.getImageData(0, 0, canvas.width, canvas.height);
|
||||
for (let i = 0; i < imageData2.data.length; i += 1){
|
||||
if (imageData1.data[i] !== imageData2.data[i]){
|
||||
log("mismatch at", i, ":",
|
||||
@ -486,9 +485,9 @@ addTest("readout - in - out test", function(log){
|
||||
addTest("window name change", function(log){
|
||||
"use strict";
|
||||
|
||||
var oldName = window.name;
|
||||
const oldName = window.name;
|
||||
log("old name:", oldName);
|
||||
var newName = oldName + " added";
|
||||
const newName = oldName + " added";
|
||||
log("new name:", newName);
|
||||
window.name = newName;
|
||||
|
||||
@ -502,7 +501,7 @@ addTest("window name change", function(log){
|
||||
function checkDOMRectData(rect, data, log){
|
||||
"use strict";
|
||||
|
||||
var detected = false;
|
||||
let detected = false;
|
||||
["x", "y", "width", "height"].forEach(function(property){
|
||||
if (data[property] !== rect[property]){
|
||||
log("Wrong value for", property, ":", data[property], "!=", rect[property]);
|
||||
@ -515,7 +514,7 @@ function checkDOMRectData(rect, data, log){
|
||||
function getRectByData(data){
|
||||
"use strict";
|
||||
|
||||
var el = document.createElement("div");
|
||||
const el = document.createElement("div");
|
||||
el.style.cssText = "position: fixed;" +
|
||||
"left: " + data.x + "px; " +
|
||||
"top: " + data.y + "px; " +
|
||||
@ -523,7 +522,7 @@ function getRectByData(data){
|
||||
"height: " + data.height + "px;";
|
||||
|
||||
document.body.appendChild(el);
|
||||
var rect = el.getBoundingClientRect();
|
||||
const rect = el.getBoundingClientRect();
|
||||
document.body.removeChild(el);
|
||||
return rect;
|
||||
}
|
||||
@ -531,41 +530,41 @@ function getRectByData(data){
|
||||
addTest("self created DOMRect", function(log){
|
||||
"use strict";
|
||||
|
||||
var data = {
|
||||
const data = {
|
||||
x: Math.PI,
|
||||
y: Math.E,
|
||||
width: Math.LOG10E,
|
||||
height: Math.LOG2E
|
||||
};
|
||||
var rect = new DOMRect(data.x, data.y, data.width, data.height);
|
||||
const rect = new DOMRect(data.x, data.y, data.width, data.height);
|
||||
return checkDOMRectData(rect, data, log);
|
||||
});
|
||||
|
||||
addTest("known DOMRect", function(log){
|
||||
"use strict";
|
||||
|
||||
var data = {
|
||||
const data = {
|
||||
x: 1 + 1/4,
|
||||
y: 2,
|
||||
width: 3,
|
||||
height: 4
|
||||
};
|
||||
|
||||
var rect = getRectByData(data);
|
||||
const rect = getRectByData(data);
|
||||
|
||||
return checkDOMRectData(rect, data, log);
|
||||
});
|
||||
addTest("changed DOMRect", function(log){
|
||||
"use strict";
|
||||
|
||||
var data = {
|
||||
const data = {
|
||||
x: Math.PI,
|
||||
y: 2,
|
||||
width: 3,
|
||||
height: 4
|
||||
};
|
||||
|
||||
var rect = getRectByData(data);
|
||||
const rect = getRectByData(data);
|
||||
rect.x = Math.PI;
|
||||
|
||||
return checkDOMRectData(rect, data, log);
|
||||
@ -573,15 +572,15 @@ addTest("changed DOMRect", function(log){
|
||||
addTest("recreated DOMRect", function(log){
|
||||
"use strict";
|
||||
|
||||
var data = {
|
||||
const data = {
|
||||
x: Math.PI,
|
||||
y: Math.E,
|
||||
width: Math.LOG10E,
|
||||
height: Math.LOG2E
|
||||
};
|
||||
|
||||
var rect = getRectByData(data);
|
||||
var rect2 = getRectByData(rect);
|
||||
const rect = getRectByData(data);
|
||||
const rect2 = getRectByData(rect);
|
||||
|
||||
return checkDOMRectData(rect2, rect, log);
|
||||
});
|
@ -1,7 +1,7 @@
|
||||
(function(){
|
||||
"use strict";
|
||||
function byteArrayToHex(arrayBuffer){
|
||||
var chunks = [];
|
||||
const chunks = [];
|
||||
(new Uint32Array(arrayBuffer)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
@ -28,7 +28,7 @@
|
||||
}
|
||||
|
||||
const properties = ["x", "y", "width", "height", "top", "left", "right", "bottom"];
|
||||
function performTest(output, callback){
|
||||
async function performTest(output, callback){
|
||||
const rects = getElements().map(function(element){
|
||||
return {
|
||||
name: element.dataset.name,
|
||||
@ -42,15 +42,10 @@
|
||||
});
|
||||
});
|
||||
|
||||
crypto.subtle.digest("SHA-256", data)
|
||||
.then(function(hash){
|
||||
output.querySelector(".hash").textContent = byteArrayToHex(hash);
|
||||
return;
|
||||
}).catch(function(error){
|
||||
output.querySelector(".hash").textContent = "Unable to compute hash: " + error;
|
||||
});
|
||||
const hash = await crypto.subtle.digest("SHA-256", data);
|
||||
output.querySelector(".hash").textContent = byteArrayToHex(hash);
|
||||
|
||||
var dataNode = output.querySelector(".data");
|
||||
const dataNode = output.querySelector(".data");
|
||||
dataNode.innerHTML = "<table><tr><th></th>" +
|
||||
rects.map(function(rect){
|
||||
return "<th>" + rect.name + "</th>";
|
||||
@ -68,22 +63,16 @@
|
||||
}).join("") + "</tr>";
|
||||
}).join("") +
|
||||
"</table>";
|
||||
rects.forEach(function(rect){
|
||||
rects.forEach(async function(rect){
|
||||
const data = new Float64Array(properties.length);
|
||||
properties.forEach(function(property, i){
|
||||
data[i] = rect.data[property];
|
||||
});
|
||||
|
||||
crypto.subtle.digest("SHA-256", data).then(function(hash){
|
||||
dataNode.querySelector(
|
||||
".rectHash[data-name=\"" + rect.name + "\"]"
|
||||
).textContent = byteArrayToHex(hash);
|
||||
return;
|
||||
}).catch(function(error){
|
||||
dataNode.querySelector(
|
||||
".rectHash[data-name=\"" + rect.name + "\"]"
|
||||
).textContent = "Unable to compute hash: " + error;
|
||||
});
|
||||
const hash = await crypto.subtle.digest("SHA-256", data);
|
||||
dataNode.querySelector(
|
||||
".rectHash[data-name=\"" + rect.name + "\"]"
|
||||
).textContent = byteArrayToHex(hash);
|
||||
});
|
||||
}
|
||||
|
||||
@ -144,12 +133,12 @@
|
||||
return element.getBoundingClientRect();
|
||||
});
|
||||
createTest("Range.getClientRects", function(element){
|
||||
var range = document.createRange();
|
||||
const range = document.createRange();
|
||||
range.selectNode(element);
|
||||
return range.getClientRects()[0];
|
||||
});
|
||||
createTest("Range.getBoundingClientRect", function(element){
|
||||
var range = document.createRange();
|
||||
const range = document.createRange();
|
||||
range.selectNode(element);
|
||||
return range.getBoundingClientRect();
|
||||
});
|
||||
|
@ -90,11 +90,11 @@
|
||||
document.body.innerHTML = "<iframe></iframe>";
|
||||
log("TEST:", "innerHTML after 1000ms:", compare(test(window[0]), reference));
|
||||
|
||||
var iFrame = document.createElement("iframe");
|
||||
const iFrame = document.createElement("iframe");
|
||||
document.body.appendChild(iFrame);
|
||||
log("TEST:", "appendChild after 1000ms:", compare(test(window[1]), reference));
|
||||
|
||||
var iFrame2 = document.createElement("iframe");
|
||||
const iFrame2 = document.createElement("iframe");
|
||||
iFrame.replaceWith(iFrame2);
|
||||
log("TEST:", "replaceWith after 1000ms:", compare(test(window[1]), reference));
|
||||
|
||||
@ -107,7 +107,7 @@
|
||||
"<li>all the displayed hashes should be the same (exception if there is a change to a wyciwyg page)</li>" +
|
||||
"<li>all lines with \"TEST:\" should have a \"match\" at the end</li>" +
|
||||
"</ul>";
|
||||
var title = document.createElement("title");
|
||||
const title = document.createElement("title");
|
||||
title.textContent = "iFrame test";
|
||||
document.getElementsByTagName("head")[0].appendChild(title);
|
||||
}, 1000);
|
||||
|
@ -1,3 +1,4 @@
|
||||
// eslint-disable-next-line no-var
|
||||
var log = function(){
|
||||
"use strict";
|
||||
return function log(...str){
|
||||
@ -18,9 +19,9 @@ function draw(canvas){
|
||||
canvas.setAttribute("width", 220);
|
||||
canvas.setAttribute("height", 30);
|
||||
|
||||
var fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
const fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.textBaseline = "top";
|
||||
ctx.font = "14px 'Arial'";
|
||||
ctx.textBaseline = "alphabetic";
|
||||
@ -37,39 +38,34 @@ function test(window){
|
||||
"use strict";
|
||||
|
||||
// create window canvas
|
||||
var canvas = document.createElement("canvas");
|
||||
const canvas = document.createElement("canvas");
|
||||
// draw image in window canvas
|
||||
draw(canvas);
|
||||
return window.HTMLCanvasElement.prototype.toDataURL.call(canvas);
|
||||
}
|
||||
|
||||
function hash(string){
|
||||
async function hash(string){
|
||||
"use strict";
|
||||
|
||||
var buffer = new TextEncoder("utf-8").encode(string);
|
||||
return crypto.subtle.digest("SHA-256", buffer).then(function(hash){
|
||||
var chunks = [];
|
||||
(new Uint32Array(hash)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
return chunks.map(function(chunk){
|
||||
return "0".repeat(8 - chunk.length) + chunk;
|
||||
}).join("");
|
||||
const buffer = new TextEncoder("utf-8").encode(string);
|
||||
const hash = await crypto.subtle.digest("SHA-256", buffer);
|
||||
const chunks = [];
|
||||
(new Uint32Array(hash)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
|
||||
return chunks.map(function(chunk){
|
||||
return "0".repeat(8 - chunk.length) + chunk;
|
||||
}).join("");
|
||||
}
|
||||
|
||||
function compare(string1, string2, alwaysOutputHashes){
|
||||
"use strict";
|
||||
function outputHashes(message){
|
||||
return Promise.all([
|
||||
async function outputHashes(message){
|
||||
const hashes = await Promise.all([
|
||||
hash(string1),
|
||||
hash(string2)
|
||||
]).then(function(hashes){
|
||||
console.log(message, ...hashes);
|
||||
return;
|
||||
});
|
||||
|
||||
]);
|
||||
console.log(message, ...hashes);
|
||||
}
|
||||
|
||||
if (string1 === string2){
|
||||
@ -100,14 +96,15 @@ function compare(string1, string2, alwaysOutputHashes){
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line no-var
|
||||
var reference = test(window);
|
||||
hash(reference).then(function(hash){
|
||||
(async function(){
|
||||
"use strict";
|
||||
|
||||
log("reference hash:", hash);
|
||||
return;
|
||||
}).catch(function(error){
|
||||
"use strict";
|
||||
|
||||
log("%cX", "color: red", "Unable to compute reference hash:", error);
|
||||
});
|
||||
try {
|
||||
const hashValue = await hash(reference);
|
||||
log("reference hash:", hashValue);
|
||||
}
|
||||
catch (error){
|
||||
log("%cX", "color: red", "Unable to compute reference hash:", error);
|
||||
}
|
||||
}());
|
@ -1,15 +1,15 @@
|
||||
|
||||
var createLog = function(){
|
||||
const createLog = function(){
|
||||
"use strict";
|
||||
|
||||
var div = document.getElementById("log");
|
||||
const div = document.getElementById("log");
|
||||
|
||||
return function createLog(){
|
||||
var logDiv = document.createElement("div");
|
||||
const logDiv = document.createElement("div");
|
||||
logDiv.className = "log";
|
||||
div.appendChild(logDiv);
|
||||
return function createLine(str){
|
||||
var logLine = document.createElement("div");
|
||||
const logLine = document.createElement("div");
|
||||
logLine.className = "logLine";
|
||||
logDiv.appendChild(logLine);
|
||||
logLine.textContent = str;
|
||||
@ -20,7 +20,7 @@ var createLog = function(){
|
||||
};
|
||||
}();
|
||||
|
||||
var log = createLog();
|
||||
let log = createLog();
|
||||
|
||||
log("user agent equal between server and client: " + (
|
||||
document.getElementById("serverUserAgent").text === navigator.userAgent
|
||||
@ -29,26 +29,26 @@ log("user agent equal between server and client: " + (
|
||||
Object.keys(navigator.__proto__).sort().forEach(function(property){
|
||||
"use strict";
|
||||
|
||||
var value = navigator[property];
|
||||
const value = navigator[property];
|
||||
if ((typeof value) === "string"){
|
||||
log(property + ": " + value);
|
||||
}
|
||||
});
|
||||
|
||||
var section = document.createElement("h2");
|
||||
const section = document.createElement("h2");
|
||||
section.textContent = "Values in iFrame";
|
||||
document.getElementById("log").append(section);
|
||||
|
||||
log = createLog();
|
||||
|
||||
var iframe = document.createElement("iframe");
|
||||
const iframe = document.createElement("iframe");
|
||||
document.body.appendChild(iframe);
|
||||
var iframeWindow = frames[frames.length - 1];
|
||||
const iframeWindow = frames[frames.length - 1];
|
||||
|
||||
Object.keys(navigator.__proto__).sort().forEach(function(property){
|
||||
"use strict";
|
||||
|
||||
var value = iframeWindow.navigator[property];
|
||||
const value = iframeWindow.navigator[property];
|
||||
if ((typeof value) === "string"){
|
||||
log(property + "@iframe: " + value);
|
||||
}
|
||||
|
@ -1,23 +1,23 @@
|
||||
|
||||
var createLog = function(){
|
||||
const createLog = function(){
|
||||
"use strict";
|
||||
|
||||
var div = document.getElementById("log");
|
||||
const div = document.getElementById("log");
|
||||
|
||||
return function createLog(){
|
||||
var logDiv = document.createElement("div");
|
||||
const logDiv = document.createElement("div");
|
||||
logDiv.className = "log";
|
||||
div.appendChild(logDiv);
|
||||
return {
|
||||
createButton: function createButton(text, callback){
|
||||
var button = document.createElement("button");
|
||||
const button = document.createElement("button");
|
||||
button.className = "logButton";
|
||||
logDiv.appendChild(button);
|
||||
button.textContent = text;
|
||||
button.addEventListener("click", callback);
|
||||
},
|
||||
createLine: function createLine(str, type = "div"){
|
||||
var logLine = document.createElement(type);
|
||||
const logLine = document.createElement(type);
|
||||
logLine.className = "logLine";
|
||||
logDiv.appendChild(logLine);
|
||||
logLine.textContent = str;
|
||||
@ -29,20 +29,20 @@ var createLog = function(){
|
||||
};
|
||||
}();
|
||||
|
||||
var performTest = function(){
|
||||
const performTest = function(){
|
||||
"use strict";
|
||||
|
||||
return function performTest(name, func, innerRunLength, outerRunLength){
|
||||
var log = createLog();
|
||||
const log = createLog();
|
||||
log.createLine("test " + name, "h3");
|
||||
var line = log.createLine("");
|
||||
var line2;
|
||||
var time = 0;
|
||||
var time2 = 0;
|
||||
var min = Number.POSITIVE_INFINITY;
|
||||
var max = 0;
|
||||
var outerI = 0;
|
||||
var outerRunIncrease = outerRunLength;
|
||||
const line = log.createLine("");
|
||||
let line2;
|
||||
let time = 0;
|
||||
let time2 = 0;
|
||||
let min = Number.POSITIVE_INFINITY;
|
||||
let max = 0;
|
||||
let outerI = 0;
|
||||
const outerRunIncrease = outerRunLength;
|
||||
if (func.prepareOnce){
|
||||
func.prepareOnce();
|
||||
}
|
||||
@ -50,21 +50,21 @@ var performTest = function(){
|
||||
line("starting");
|
||||
line2("");
|
||||
function run(){
|
||||
for (var i = 0; i < innerRunLength; i += 1){
|
||||
for (let i = 0; i < innerRunLength; i += 1){
|
||||
if (func.prepare){
|
||||
func.prepare();
|
||||
}
|
||||
var start = performance.now();
|
||||
const start = performance.now();
|
||||
func.test();
|
||||
var end = performance.now();
|
||||
var duration = end - start;
|
||||
const end = performance.now();
|
||||
const duration = end - start;
|
||||
min = Math.min(min, duration);
|
||||
max = Math.max(max, duration);
|
||||
time2 += duration * duration;
|
||||
time += duration;
|
||||
}
|
||||
outerI += 1;
|
||||
var totalRunI = outerI * innerRunLength;
|
||||
const totalRunI = outerI * innerRunLength;
|
||||
line(
|
||||
"finished run " + totalRunI + " from " + (innerRunLength * outerRunLength) +
|
||||
" -> average: " + (time / totalRunI).toFixed(2) +
|
||||
@ -91,9 +91,9 @@ function draw(canvas){
|
||||
canvas.setAttribute("width", 220);
|
||||
canvas.setAttribute("height", 30);
|
||||
|
||||
var fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
const fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.textBaseline = "top";
|
||||
ctx.font = "14px 'Arial'";
|
||||
ctx.textBaseline = "alphabetic";
|
||||
@ -107,10 +107,10 @@ function draw(canvas){
|
||||
return ctx;
|
||||
}
|
||||
|
||||
var fingerprintTest = function(){
|
||||
const fingerprintTest = function(){
|
||||
"use strict";
|
||||
|
||||
var canvas;
|
||||
let canvas;
|
||||
return {
|
||||
prepare: function(){
|
||||
// create window canvas
|
||||
@ -124,20 +124,20 @@ var fingerprintTest = function(){
|
||||
};
|
||||
}();
|
||||
|
||||
var randomImageTest = function(){
|
||||
const randomImageTest = function(){
|
||||
"use strict";
|
||||
|
||||
var canvas;
|
||||
let canvas;
|
||||
|
||||
return {
|
||||
prepare: function(){
|
||||
canvas = document.createElement("canvas");
|
||||
canvas.width = 1000;
|
||||
canvas.height = 100;
|
||||
var imageData = new ImageData(canvas.width, canvas.height);
|
||||
var data = imageData.data;
|
||||
const imageData = new ImageData(canvas.width, canvas.height);
|
||||
const data = imageData.data;
|
||||
|
||||
for (var i = 0; i < data.length; i += 4){
|
||||
for (let i = 0; i < data.length; i += 4){
|
||||
data[i + 0] = Math.floor(256 * Math.random());
|
||||
data[i + 1] = Math.floor(256 * Math.random());
|
||||
data[i + 2] = Math.floor(256 * Math.random());
|
||||
@ -152,10 +152,10 @@ var randomImageTest = function(){
|
||||
};
|
||||
}();
|
||||
|
||||
var innerHTMlTest = function(html, repeats){
|
||||
const innerHTMlTest = function(html, repeats){
|
||||
"use strict";
|
||||
|
||||
var div;
|
||||
let div;
|
||||
|
||||
return {
|
||||
prepareOnce: function(){
|
||||
@ -164,7 +164,7 @@ var innerHTMlTest = function(html, repeats){
|
||||
document.body.appendChild(div);
|
||||
},
|
||||
test: function randomImageTest(){
|
||||
for (var i = repeats; i--;){
|
||||
for (let i = repeats; i--;){
|
||||
div.innerHTML = html;
|
||||
div.innerHTML = "";
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
function addTest(container, title, callback){
|
||||
"use strict";
|
||||
|
||||
var testContainer = document.createElement("div");
|
||||
const testContainer = document.createElement("div");
|
||||
testContainer.className = "test";
|
||||
|
||||
var titleNode = document.createElement("h3");
|
||||
const titleNode = document.createElement("h3");
|
||||
titleNode.textContent = title;
|
||||
testContainer.appendChild(titleNode);
|
||||
|
||||
var resultsNode = document.createElement("div");
|
||||
const resultsNode = document.createElement("div");
|
||||
resultsNode.className = "results";
|
||||
testContainer.appendChild(resultsNode);
|
||||
|
||||
@ -22,27 +22,28 @@ function addConsistencyTest(title, callback){
|
||||
|
||||
addTest(document.getElementById("consistency"), title, function(resultsNode){
|
||||
|
||||
var line = document.createElement("div");
|
||||
const line = document.createElement("div");
|
||||
line.textContent = "consistent: ";
|
||||
resultsNode.appendChild(line);
|
||||
|
||||
var consistent = document.createElement("span");
|
||||
const consistent = document.createElement("span");
|
||||
consistent.className = "result";
|
||||
line.appendChild(consistent);
|
||||
|
||||
function compute(){
|
||||
async function compute(){
|
||||
consistent.textContent = "computing";
|
||||
callback().then(function(value){
|
||||
try {
|
||||
const value = await callback();
|
||||
consistent.textContent = value? "OK": "not OK";
|
||||
return;
|
||||
}).catch(function(error){
|
||||
}
|
||||
catch (error){
|
||||
consistent.classList.add("failed");
|
||||
if (Array.isArray(error)){
|
||||
consistent.textContent = "";
|
||||
var ul = document.createElement("ul");
|
||||
const ul = document.createElement("ul");
|
||||
consistent.appendChild(ul);
|
||||
error.forEach(function(error){
|
||||
var li = document.createElement("li");
|
||||
const li = document.createElement("li");
|
||||
li.textContent = error;
|
||||
ul.appendChild(li);
|
||||
});
|
||||
@ -50,7 +51,7 @@ function addConsistencyTest(title, callback){
|
||||
else {
|
||||
consistent.textContent = error;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
compute();
|
||||
window.addEventListener("resize", compute);
|
||||
@ -76,7 +77,7 @@ addConsistencyTest("screen properties", function(){
|
||||
addConsistencyTest("media queries: window.matchMedia - low value", function(){
|
||||
"use strict";
|
||||
|
||||
var value = Math.floor(Math.min(screen.width, screen.height) / 2);
|
||||
const value = Math.floor(Math.min(screen.width, screen.height) / 2);
|
||||
|
||||
return Promise.resolve(
|
||||
window.matchMedia("(min-device-width: " + value + "px)").matches &&
|
||||
@ -89,11 +90,11 @@ addConsistencyTest("media queries: window.matchMedia - low value", function(){
|
||||
addConsistencyTest("media queries: window.matchMedia - reported value", function(){
|
||||
"use strict";
|
||||
|
||||
var errors = [];
|
||||
const errors = [];
|
||||
["width", "height"].forEach(function(dimension){
|
||||
["min-", "max-", ""].forEach(function(comparison){
|
||||
var queryBase = "(" + comparison + "device-" + dimension + ": ";
|
||||
var query = queryBase + screen[dimension] + "px)";
|
||||
const queryBase = "(" + comparison + "device-" + dimension + ": ";
|
||||
let query = queryBase + screen[dimension] + "px)";
|
||||
if (!window.matchMedia(query).matches){
|
||||
errors.push(query + " did not match.");
|
||||
query = queryBase + (screen[dimension] + 1) + "px)";
|
||||
@ -116,7 +117,7 @@ addConsistencyTest("media queries: window.matchMedia - reported value", function
|
||||
addConsistencyTest("media queries: window.matchMedia - big value", function(){
|
||||
"use strict";
|
||||
|
||||
var value = Math.max(screen.width, screen.height) * 2;
|
||||
const value = Math.max(screen.width, screen.height) * 2;
|
||||
|
||||
return Promise.resolve(
|
||||
!window.matchMedia("(min-device-width: " + value + "px)").matches &&
|
||||
@ -126,27 +127,28 @@ addConsistencyTest("media queries: window.matchMedia - big value", function(){
|
||||
);
|
||||
});
|
||||
|
||||
var addResolutionTest = function(){
|
||||
const addResolutionTest = function(){
|
||||
"use strict";
|
||||
return function addResolutionTest(title, callback, properties = ["width", "height"]){
|
||||
addTest(document.getElementById("resolution"), title, function(resultsNode){
|
||||
properties.forEach(function(type){
|
||||
var line = document.createElement("div");
|
||||
const line = document.createElement("div");
|
||||
line.textContent = type + ": ";
|
||||
resultsNode.appendChild(line);
|
||||
|
||||
var number = document.createElement("span");
|
||||
const number = document.createElement("span");
|
||||
number.className = "result " + type;
|
||||
line.appendChild(number);
|
||||
function compute(){
|
||||
async function compute(){
|
||||
number.textContent = "computing";
|
||||
callback(type).then(function(value){
|
||||
try {
|
||||
const value = await callback(type);
|
||||
number.textContent = value;
|
||||
return;
|
||||
}).catch(function(error){
|
||||
}
|
||||
catch (error){
|
||||
number.classList.add("failed");
|
||||
number.textContent = error;
|
||||
});
|
||||
}
|
||||
}
|
||||
compute();
|
||||
window.addEventListener("resize", compute);
|
||||
@ -185,78 +187,76 @@ addResolutionTest("window properties: outer...", function(type){
|
||||
]);
|
||||
});
|
||||
|
||||
function searchValue(tester){
|
||||
async function searchValue(tester){
|
||||
"use strict";
|
||||
|
||||
var minValue = 0;
|
||||
var maxValue = 512;
|
||||
var ceiling = Math.pow(2, 32);
|
||||
let minValue = 0;
|
||||
let maxValue = 512;
|
||||
const ceiling = Math.pow(2, 32);
|
||||
|
||||
function stepUp(){
|
||||
async function stepUp(){
|
||||
if (maxValue > ceiling){
|
||||
return Promise.reject("Unable to find upper bound");
|
||||
}
|
||||
return tester(maxValue).then(function(testResult){
|
||||
if (testResult === searchValue.isEqual){
|
||||
return maxValue;
|
||||
}
|
||||
else if (testResult === searchValue.isBigger){
|
||||
minValue = maxValue;
|
||||
maxValue *= 2;
|
||||
return stepUp();
|
||||
const testResult = await tester(maxValue);
|
||||
if (testResult === searchValue.isEqual){
|
||||
return maxValue;
|
||||
}
|
||||
else if (testResult === searchValue.isBigger){
|
||||
minValue = maxValue;
|
||||
maxValue *= 2;
|
||||
return stepUp();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let v = 1;
|
||||
async function test(){
|
||||
const r = await tester(v);
|
||||
v = 2;
|
||||
}
|
||||
async function binarySearch(){
|
||||
if (maxValue - minValue < 0.01){
|
||||
const testResult = await tester(minValue);
|
||||
if (testResult.isEqual){
|
||||
return minValue;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
function binarySearch(){
|
||||
if (maxValue - minValue < 0.01){
|
||||
return tester(minValue).then(function(testResult){
|
||||
const testResult = await tester(maxValue);
|
||||
if (testResult.isEqual){
|
||||
return minValue;
|
||||
return maxValue;
|
||||
}
|
||||
else {
|
||||
// eslint-disable-next-line promise/no-nesting
|
||||
return tester(maxValue).then(function(testResult){
|
||||
if (testResult.isEqual){
|
||||
return maxValue;
|
||||
}
|
||||
else {
|
||||
throw "Search could not find exact value." +
|
||||
" It's between " + minValue + " and " + maxValue + ".";
|
||||
}
|
||||
});
|
||||
throw "Search could not find exact value." +
|
||||
" It's between " + minValue + " and " + maxValue + ".";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
var pivot = (minValue + maxValue) / 2;
|
||||
return tester(pivot).then(function(testResult){
|
||||
if (testResult === searchValue.isEqual){
|
||||
return pivot;
|
||||
}
|
||||
else if (testResult === searchValue.isBigger){
|
||||
minValue = pivot;
|
||||
return binarySearch();
|
||||
}
|
||||
else {
|
||||
maxValue = pivot;
|
||||
return binarySearch();
|
||||
}
|
||||
});
|
||||
const pivot = (minValue + maxValue) / 2;
|
||||
const testResult = await tester(pivot);
|
||||
if (testResult === searchValue.isEqual){
|
||||
return pivot;
|
||||
}
|
||||
else if (testResult === searchValue.isBigger){
|
||||
minValue = pivot;
|
||||
return binarySearch();
|
||||
}
|
||||
else {
|
||||
maxValue = pivot;
|
||||
return binarySearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stepUp().then(function(stepUpResult){
|
||||
if (stepUpResult){
|
||||
return stepUpResult;
|
||||
}
|
||||
else {
|
||||
return binarySearch();
|
||||
}
|
||||
});
|
||||
|
||||
const stepUpResult = await stepUp();
|
||||
if (stepUpResult){
|
||||
return stepUpResult;
|
||||
}
|
||||
else {
|
||||
return binarySearch();
|
||||
}
|
||||
}
|
||||
searchValue.isSmaller = -1;
|
||||
searchValue.isEqual = 0;
|
||||
@ -297,16 +297,16 @@ addResolutionTest("media queries: window.matchMedia (min)", function(type){
|
||||
addResolutionTest("media queries: css (max)", function(type){
|
||||
"use strict";
|
||||
|
||||
var tester = document.createElement("div");
|
||||
var id = "tester_" + (Math.random() * Math.pow(2, 32)).toString(36).replace(".", "_");
|
||||
const tester = document.createElement("div");
|
||||
const id = "tester_" + (Math.random() * Math.pow(2, 32)).toString(36).replace(".", "_");
|
||||
tester.id = id;
|
||||
document.body.appendChild(tester);
|
||||
|
||||
var style = document.createElement("style");
|
||||
const style = document.createElement("style");
|
||||
style.type = "text/css";
|
||||
document.head.appendChild(style);
|
||||
|
||||
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
||||
const styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
||||
styleSheet.insertRule("#" + id + "{position: fixed; right: 100%; z-index: 0;}");
|
||||
|
||||
return searchValue(function(valueToTest){
|
||||
@ -314,12 +314,12 @@ addResolutionTest("media queries: css (max)", function(type){
|
||||
while (styleSheet.rules.length > 1){
|
||||
styleSheet.removeRule(1);
|
||||
}
|
||||
var rule = "@media (max-device-" + type + ": " + valueToTest + "px){#" + id + "{z-index: 1;}}";
|
||||
let rule = "@media (max-device-" + type + ": " + valueToTest + "px){#" + id + "{z-index: 1;}}";
|
||||
styleSheet.insertRule(rule, 1);
|
||||
rule = "@media (device-" + type + ": " + valueToTest + "px){#" + id + "{z-index: 2;}}";
|
||||
styleSheet.insertRule(rule, 2);
|
||||
window.setTimeout(function(){
|
||||
var testValue = window.getComputedStyle(tester).zIndex;
|
||||
const testValue = window.getComputedStyle(tester).zIndex;
|
||||
switch (testValue){
|
||||
case "0":
|
||||
resolve(searchValue.isBigger);
|
||||
@ -341,16 +341,16 @@ addResolutionTest("media queries: css (max)", function(type){
|
||||
addResolutionTest("media queries: css (min)", function(type){
|
||||
"use strict";
|
||||
|
||||
var tester = document.createElement("div");
|
||||
var id = "tester_" + (Math.random() * Math.pow(2, 32)).toString(36).replace(".", "_");
|
||||
const tester = document.createElement("div");
|
||||
const id = "tester_" + (Math.random() * Math.pow(2, 32)).toString(36).replace(".", "_");
|
||||
tester.id = id;
|
||||
document.body.appendChild(tester);
|
||||
|
||||
var style = document.createElement("style");
|
||||
const style = document.createElement("style");
|
||||
style.type = "text/css";
|
||||
document.head.appendChild(style);
|
||||
|
||||
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
||||
const styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
||||
styleSheet.insertRule("#" + id + "{position: fixed; right: 100%; z-index: 0;}");
|
||||
|
||||
return searchValue(function(valueToTest){
|
||||
@ -358,12 +358,12 @@ addResolutionTest("media queries: css (min)", function(type){
|
||||
while (styleSheet.rules.length > 1){
|
||||
styleSheet.removeRule(1);
|
||||
}
|
||||
var rule = "@media (min-device-" + type + ": " + valueToTest + "px){#" + id + "{z-index: 1;}}";
|
||||
let rule = "@media (min-device-" + type + ": " + valueToTest + "px){#" + id + "{z-index: 1;}}";
|
||||
styleSheet.insertRule(rule, 1);
|
||||
rule = "@media (device-" + type + ": " + valueToTest + "px){#" + id + "{z-index: 2;}}";
|
||||
styleSheet.insertRule(rule, 2);
|
||||
window.setTimeout(function(){
|
||||
var testValue = window.getComputedStyle(tester).zIndex;
|
||||
const testValue = window.getComputedStyle(tester).zIndex;
|
||||
switch (testValue){
|
||||
case "0":
|
||||
resolve(searchValue.isSmaller);
|
||||
|
@ -14,9 +14,9 @@
|
||||
canvas.setAttribute("width", 220);
|
||||
canvas.setAttribute("height", 30);
|
||||
|
||||
var fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
const fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.textBaseline = "top";
|
||||
ctx.font = "14px 'Arial'";
|
||||
ctx.textBaseline = "alphabetic";
|
||||
@ -33,9 +33,9 @@
|
||||
"use strict";
|
||||
|
||||
// create window canvas
|
||||
var canvas = document.createElement("canvas");
|
||||
const canvas = document.createElement("canvas");
|
||||
// draw image in window canvas
|
||||
var ctx = draw(canvas);
|
||||
const ctx = draw(canvas);
|
||||
return {
|
||||
imageData: ctx.getImageData(0, 0, canvas.width, canvas.height),
|
||||
url: canvas.toDataURL(),
|
||||
@ -56,7 +56,7 @@
|
||||
function hashToString(hash){
|
||||
"use strict";
|
||||
|
||||
var chunks = [];
|
||||
const chunks = [];
|
||||
(new Uint32Array(hash)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
@ -65,47 +65,45 @@
|
||||
}).join("");
|
||||
}
|
||||
|
||||
var send = function(){
|
||||
const send = function(){
|
||||
"use strict";
|
||||
|
||||
return function send(form, {url, imageData, isPointInPath}){
|
||||
var buffer = new TextEncoder("utf-8").encode(url);
|
||||
return Promise.all([
|
||||
return async function send(form, {url, imageData, isPointInPath}){
|
||||
const buffer = new TextEncoder("utf-8").encode(url);
|
||||
const hashes = await Promise.all([
|
||||
crypto.subtle.digest("SHA-256", buffer),
|
||||
crypto.subtle.digest("SHA-256", imageData.data)
|
||||
]).then(function(hashes){
|
||||
var data = JSON.stringify({
|
||||
urlHash: hashToString(hashes[0]),
|
||||
imageDataHash: hashToString(hashes[1]),
|
||||
isPointInPath
|
||||
}, null, "\t");
|
||||
form.fingerprint.value = data;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", form.action, true);
|
||||
xhr.onreadystatechange = function(){
|
||||
if (this.readyState === 4){
|
||||
const status = this.status;
|
||||
if (status === 200 || status === 304) {
|
||||
console.log("Sending xhr successful from", origin, ":", data);
|
||||
}
|
||||
else {
|
||||
console.log("Sending xhr failed:", this);
|
||||
}
|
||||
]);
|
||||
const data = JSON.stringify({
|
||||
urlHash: hashToString(hashes[0]),
|
||||
imageDataHash: hashToString(hashes[1]),
|
||||
isPointInPath
|
||||
}, null, "\t");
|
||||
form.fingerprint.value = data;
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", form.action, true);
|
||||
xhr.onreadystatechange = function(){
|
||||
if (this.readyState === 4){
|
||||
const status = this.status;
|
||||
if (status === 200 || status === 304) {
|
||||
console.log("Sending xhr successful from", origin, ":", data);
|
||||
}
|
||||
};
|
||||
xhr.send(new FormData(form));
|
||||
window.setTimeout(function(){
|
||||
form.submit();
|
||||
window.setTimeout(
|
||||
function(){
|
||||
document.getElementById("log").textContent =
|
||||
"You see the real canvas fingerprint, but it cannot leak from this iFrame.";
|
||||
},
|
||||
250
|
||||
);
|
||||
}, 1000);
|
||||
return;
|
||||
});
|
||||
else {
|
||||
console.log("Sending xhr failed:", this);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send(new FormData(form));
|
||||
window.setTimeout(function(){
|
||||
form.submit();
|
||||
window.setTimeout(
|
||||
function(){
|
||||
document.getElementById("log").textContent =
|
||||
"You see the real canvas fingerprint, but it cannot leak from this iFrame.";
|
||||
},
|
||||
250
|
||||
);
|
||||
}, 1000);
|
||||
};
|
||||
}();
|
||||
|
||||
|
@ -6,16 +6,16 @@
|
||||
<link href="testIcon.svg" type="image/png" rel="icon">
|
||||
<link href="testIcon.svg" type="image/png" rel="shortcut icon">
|
||||
<script>
|
||||
var firstDescriptor = Object.getOwnPropertyDescriptor(HTMLCanvasElement.prototype, "getContext");
|
||||
const firstDescriptor = Object.getOwnPropertyDescriptor(HTMLCanvasElement.prototype, "getContext");
|
||||
console.log(new Date(), "starting first fingerprint", window.name);
|
||||
function fingerPrint(){
|
||||
"use strict";var canvas = document.createElement("canvas");
|
||||
"use strict";const canvas = document.createElement("canvas");
|
||||
canvas.setAttribute("width", 220);
|
||||
canvas.setAttribute("height", 30);
|
||||
|
||||
var fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
const fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.textBaseline = "top";
|
||||
ctx.font = "14px 'Arial'";
|
||||
ctx.textBaseline = "alphabetic";
|
||||
@ -28,21 +28,20 @@
|
||||
|
||||
return canvas.toDataURL();
|
||||
}
|
||||
function hash(url){
|
||||
async function hash(url){
|
||||
"use strict";
|
||||
|
||||
var buffer = new TextEncoder("utf-8").encode(url);
|
||||
return crypto.subtle.digest("SHA-256", buffer).then(function(hash){
|
||||
var chunks = [];
|
||||
(new Uint32Array(hash)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
return chunks.map(function(chunk){
|
||||
return "0".repeat(8 - chunk.length) + chunk;
|
||||
}).join("");
|
||||
const buffer = new TextEncoder("utf-8").encode(url);
|
||||
const hash = await crypto.subtle.digest("SHA-256", buffer);
|
||||
const chunks = [];
|
||||
(new Uint32Array(hash)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
return chunks.map(function(chunk){
|
||||
return "0".repeat(8 - chunk.length) + chunk;
|
||||
}).join("");
|
||||
}
|
||||
var firstFingerprint = false;
|
||||
let firstFingerprint = false;
|
||||
try {
|
||||
firstFingerprint = fingerPrint();
|
||||
}
|
||||
@ -76,14 +75,14 @@
|
||||
<div id="output"></div>
|
||||
<script>
|
||||
if (firstFingerprint){
|
||||
var output = document.getElementById("output");
|
||||
const output = document.getElementById("output");
|
||||
output.textContent = "context API not blocked";
|
||||
output.appendChild(document.createElement("br"));
|
||||
window.setTimeout(function(){
|
||||
window.setTimeout(async function(){
|
||||
"use strict";
|
||||
|
||||
console.log(new Date(), "starting second fingerprint", window.name);
|
||||
var secondDescriptor = Object.getOwnPropertyDescriptor(HTMLCanvasElement.prototype, "getContext");
|
||||
const secondDescriptor = Object.getOwnPropertyDescriptor(HTMLCanvasElement.prototype, "getContext");
|
||||
|
||||
if (firstDescriptor.value === secondDescriptor.value){
|
||||
output.appendChild(document.createTextNode("descriptor did not change -> good!"));
|
||||
@ -95,26 +94,22 @@
|
||||
output.classList.add("nok");
|
||||
}
|
||||
output.appendChild(document.createElement("br"));
|
||||
var secondFingerprint = fingerPrint();
|
||||
const secondFingerprint = fingerPrint();
|
||||
if (firstFingerprint === secondFingerprint){
|
||||
return hash(firstFingerprint).then(function(hash){
|
||||
output.appendChild(document.createTextNode("fingerprint consistent (" + hash + ") -> good!"));
|
||||
output.classList.add("ok");
|
||||
return;
|
||||
});
|
||||
const firstHash = await hash(firstFingerprint);
|
||||
output.appendChild(document.createTextNode("fingerprint consistent (" + firstHash + ") -> good!"));
|
||||
output.classList.add("ok");
|
||||
}
|
||||
else {
|
||||
return Promise.all([hash(firstFingerprint), hash(secondFingerprint)]).then(function(hashes){
|
||||
output.appendChild(
|
||||
document.createTextNode(
|
||||
"fingerprint not consistent (" +
|
||||
hashes[0] + " != " + hashes[1] +
|
||||
") -> very bad! (potential fingerprint leak)"
|
||||
)
|
||||
);
|
||||
output.classList.add("nok");
|
||||
return;
|
||||
});
|
||||
const hashes = await Promise.all([hash(firstFingerprint), hash(secondFingerprint)]);
|
||||
output.appendChild(
|
||||
document.createTextNode(
|
||||
"fingerprint not consistent (" +
|
||||
hashes[0] + " != " + hashes[1] +
|
||||
") -> very bad! (potential fingerprint leak)"
|
||||
)
|
||||
);
|
||||
output.classList.add("nok");
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
58
test/test.js
58
test/test.js
@ -2,7 +2,7 @@
|
||||
"use strict";
|
||||
|
||||
function hashToString(hash){
|
||||
var chunks = [];
|
||||
const chunks = [];
|
||||
(new Uint32Array(hash)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
@ -11,22 +11,18 @@
|
||||
}).join("");
|
||||
}
|
||||
|
||||
function show(container, {url, imageData, isPointInPath}){
|
||||
var display = container.querySelector(".display");
|
||||
async function show(container, {url, imageData, isPointInPath}){
|
||||
const display = container.querySelector(".display");
|
||||
display.src = url;
|
||||
display.title = url;
|
||||
var buffer = new TextEncoder("utf-8").encode(url);
|
||||
Promise.all([
|
||||
const buffer = new TextEncoder("utf-8").encode(url);
|
||||
const hashes = await Promise.all([
|
||||
crypto.subtle.digest("SHA-256", buffer),
|
||||
crypto.subtle.digest("SHA-256", imageData.data)
|
||||
]).then(function(hashes){
|
||||
container.querySelector(".hash").textContent =
|
||||
hashToString(hashes[0]) + " / " +
|
||||
hashToString(hashes[1]);
|
||||
return;
|
||||
}).catch(function(error){
|
||||
container.querySelector(".hash").textContent = "Error while calculating hash: " + error;
|
||||
});
|
||||
]);
|
||||
container.querySelector(".hash").textContent =
|
||||
hashToString(hashes[0]) + " / " +
|
||||
hashToString(hashes[1]);
|
||||
container.querySelector(".isPointInPath").textContent = isPointInPath;
|
||||
}
|
||||
|
||||
@ -47,7 +43,7 @@
|
||||
catch (error){console.error(error);}
|
||||
window.addEventListener("click", function windowOpenTest(){
|
||||
window.removeEventListener("click", windowOpenTest);
|
||||
var newWindow = window.open("/");
|
||||
const newWindow = window.open("/");
|
||||
try{
|
||||
show(document.getElementById("windowOpen"), copyToDifferentDocumentTest(newWindow.document));
|
||||
}
|
||||
@ -79,7 +75,7 @@
|
||||
show(document.getElementById("iframe6"), dynamicIframeTest3());
|
||||
});
|
||||
document.querySelector("#windowOpen button").addEventListener("click", function(){
|
||||
var newWindow = window.open("/");
|
||||
const newWindow = window.open("/");
|
||||
show(document.getElementById("windowOpen"), copyToDifferentDocumentTest(newWindow.document));
|
||||
newWindow.close();
|
||||
});
|
||||
@ -91,9 +87,9 @@ function draw(canvas){
|
||||
canvas.setAttribute("width", 220);
|
||||
canvas.setAttribute("height", 30);
|
||||
|
||||
var fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
const fp_text = "BrowserLeaks,com <canvas> 10";
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.textBaseline = "top";
|
||||
ctx.font = "14px 'Arial'";
|
||||
ctx.textBaseline = "alphabetic";
|
||||
@ -123,9 +119,9 @@ function topTest(){
|
||||
"use strict";
|
||||
|
||||
// create window canvas
|
||||
var canvas = document.createElement("canvas");
|
||||
const canvas = document.createElement("canvas");
|
||||
// draw image in window canvas
|
||||
var ctx = draw(canvas);
|
||||
const ctx = draw(canvas);
|
||||
return {
|
||||
imageData: ctx.getImageData(0, 0, canvas.width, canvas.height),
|
||||
url: canvas.toDataURL(),
|
||||
@ -137,16 +133,16 @@ function copyToDifferentDocumentTest(otherDocument){
|
||||
"use strict";
|
||||
|
||||
// create window canvas
|
||||
var canvas = document.createElement("canvas");
|
||||
const canvas = document.createElement("canvas");
|
||||
|
||||
// draw image in window canvas
|
||||
draw(canvas);
|
||||
|
||||
// create other canvas and context
|
||||
var otherCanvas = otherDocument.createElement("canvas");
|
||||
const otherCanvas = otherDocument.createElement("canvas");
|
||||
otherCanvas.setAttribute("width", 220);
|
||||
otherCanvas.setAttribute("height", 30);
|
||||
var otherContext = otherCanvas.getContext("2d");
|
||||
const otherContext = otherCanvas.getContext("2d");
|
||||
|
||||
// copy image from window canvas to iframe context
|
||||
otherContext.drawImage(canvas, 0, 0);
|
||||
@ -167,10 +163,10 @@ function iframeTest(iframe){
|
||||
function dynamicIframeTest1(){
|
||||
"use strict";
|
||||
|
||||
var length = frames.length;
|
||||
var iframe = document.createElement("iframe");
|
||||
const length = frames.length;
|
||||
const iframe = document.createElement("iframe");
|
||||
document.body.appendChild(iframe);
|
||||
var iframeWindow = frames[length];
|
||||
const iframeWindow = frames[length];
|
||||
document.body.removeChild(iframe);
|
||||
return copyToDifferentDocumentTest(iframeWindow.document);
|
||||
}
|
||||
@ -178,10 +174,10 @@ function dynamicIframeTest1(){
|
||||
function dynamicIframeTest2(){
|
||||
"use strict";
|
||||
|
||||
var length = window.length;
|
||||
var iframe = document.createElement("iframe");
|
||||
const length = window.length;
|
||||
const iframe = document.createElement("iframe");
|
||||
document.body.appendChild(iframe);
|
||||
var iframeWindow = window[length];
|
||||
const iframeWindow = window[length];
|
||||
document.body.removeChild(iframe);
|
||||
return copyToDifferentDocumentTest(iframeWindow.document);
|
||||
}
|
||||
@ -189,11 +185,11 @@ function dynamicIframeTest2(){
|
||||
function dynamicIframeTest3(){
|
||||
"use strict";
|
||||
|
||||
var length = window.length;
|
||||
var div = document.createElement("div");
|
||||
const length = window.length;
|
||||
const div = document.createElement("div");
|
||||
document.body.appendChild(div);
|
||||
div.innerHTML = "<iframe></iframe>";
|
||||
var iframeWindow = window[length];
|
||||
const iframeWindow = window[length];
|
||||
document.body.removeChild(div);
|
||||
return copyToDifferentDocumentTest(iframeWindow.document);
|
||||
}
|
@ -3,9 +3,9 @@
|
||||
|
||||
function getParameters(context){
|
||||
const parameters = [];
|
||||
for (var name in context){
|
||||
for (let name in context){
|
||||
if (name.toUpperCase() === name){
|
||||
var value = context.getParameter(context[name]);
|
||||
const value = context.getParameter(context[name]);
|
||||
if (value !== null){
|
||||
parameters.push({name: name, value: value});
|
||||
}
|
||||
@ -13,18 +13,18 @@
|
||||
}
|
||||
const debugExtension = context.getExtension("WEBGL_debug_renderer_info");
|
||||
|
||||
for (name in debugExtension){
|
||||
for (let name in debugExtension){
|
||||
if (name.toUpperCase() === name){
|
||||
value = context.getParameter(debugExtension[name]);
|
||||
const value = context.getParameter(debugExtension[name]);
|
||||
if (value !== null){
|
||||
parameters.push({name: name, value: value});
|
||||
}
|
||||
}
|
||||
}
|
||||
var frontParameters = ["VENDOR", "RENDERER", "UNMASKED_VENDOR_WEBGL", "UNMASKED_RENDERER_WEBGL"];
|
||||
const frontParameters = ["VENDOR", "RENDERER", "UNMASKED_VENDOR_WEBGL", "UNMASKED_RENDERER_WEBGL"];
|
||||
parameters.sort(function(a, b){
|
||||
var frontA = frontParameters.indexOf(a.name);
|
||||
var frontB = frontParameters.indexOf(b.name);
|
||||
const frontA = frontParameters.indexOf(a.name);
|
||||
const frontB = frontParameters.indexOf(b.name);
|
||||
if (frontA !== -1){
|
||||
if (frontB !== -1){
|
||||
return frontA - frontB;
|
||||
@ -45,58 +45,55 @@
|
||||
return parameters;
|
||||
}
|
||||
|
||||
["webgl", "webgl2"].forEach(function(context, index){
|
||||
var output = document.createElement("div");
|
||||
["webgl", "webgl2"].forEach(async function(context, index){
|
||||
const output = document.createElement("div");
|
||||
document.getElementById("output").appendChild(output);
|
||||
try {
|
||||
var canvas = document.createElement("canvas");
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = 11;
|
||||
canvas.height = 13;
|
||||
var gl = canvas.getContext(context) || canvas.getContext("experimental-" + context);
|
||||
const gl = canvas.getContext(context) || canvas.getContext("experimental-" + context);
|
||||
|
||||
// paint it completely black
|
||||
gl.clearColor(index * 0.25, index * 0.25, index * 0.25, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
var pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4);
|
||||
const pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4);
|
||||
gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
|
||||
var values = {};
|
||||
var max = 0;
|
||||
for (var i = 0; i < pixels.length; i += 1){
|
||||
const values = {};
|
||||
let max = 0;
|
||||
for (let i = 0; i < pixels.length; i += 1){
|
||||
values[pixels[i]] = (values[pixels[i]] || 0) + 1;
|
||||
max = Math.max(max, values[pixels[i]]);
|
||||
}
|
||||
|
||||
const parameters = getParameters(gl);
|
||||
if (context === "webgl2"){
|
||||
var parameterOutput = document.createElement("table");
|
||||
const parameterOutput = document.createElement("table");
|
||||
document.getElementById("parameters").appendChild(parameterOutput);
|
||||
parameters.forEach(function(parameter){
|
||||
var parameterRow = document.createElement("tr");
|
||||
const parameterRow = document.createElement("tr");
|
||||
parameterRow.innerHTML = "<td>" + parameter.name + "</td><td>" + parameter.value + "</td>";
|
||||
parameterOutput.appendChild(parameterRow);
|
||||
});
|
||||
}
|
||||
crypto.subtle.digest("SHA-256", new TextEncoder("utf-8").encode(parameters.map(function(parameter){
|
||||
return parameter.name + ": " + parameter.value;
|
||||
}).join(",")))
|
||||
.then(function(hash){
|
||||
var chunks = [];
|
||||
(new Uint32Array(hash)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
return chunks.map(function(chunk){
|
||||
return "0".repeat(8 - chunk.length) + chunk;
|
||||
}).join("");
|
||||
}).then(function(hash) {
|
||||
output.textContent = context + ": " +
|
||||
(max !== 3 * values[255]? "": "not ") + "supported " +
|
||||
"(parameter hash: " + hash + ")";
|
||||
output.title = JSON.stringify(values);
|
||||
return;
|
||||
}).catch(function(error){
|
||||
output.textContent = "Error while calculating hash: " + error;
|
||||
});
|
||||
const hashBytes = await crypto.subtle.digest("SHA-256", new TextEncoder("utf-8")
|
||||
.encode(parameters.map(function(parameter){
|
||||
return parameter.name + ": " + parameter.value;
|
||||
}).join(",")));
|
||||
|
||||
const chunks = [];
|
||||
(new Uint32Array(hashBytes)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
const hash = chunks.map(function(chunk){
|
||||
return "0".repeat(8 - chunk.length) + chunk;
|
||||
}).join("");
|
||||
|
||||
output.textContent = context + ": " +
|
||||
(max !== 3 * values[255]? "": "not ") + "supported " +
|
||||
"(parameter hash: " + hash + ")";
|
||||
output.title = JSON.stringify(values);
|
||||
}
|
||||
catch (error){
|
||||
output.textContent = context + ": ERROR";
|
||||
|
Loading…
x
Reference in New Issue
Block a user