test updates

This commit is contained in:
kkapsner 2019-12-16 19:27:28 +01:00
parent 717e1d3e3a
commit 0d0e3e30ec
16 changed files with 455 additions and 497 deletions

View File

@ -53,6 +53,7 @@
"no-useless-rename": "warn", "no-useless-rename": "warn",
"no-var": "error", "no-var": "error",
"quotes": ["error", "double"], "quotes": ["error", "double"],
"require-atomic-updates": "off",
"semi": ["error", "always"], "semi": ["error", "always"],
"space-in-parens": ["error", "never"], "space-in-parens": ["error", "never"],
"strict": ["error", "function"], "strict": ["error", "function"],
@ -68,7 +69,6 @@
{ {
"files": ["test/*"], "files": ["test/*"],
"rules": { "rules": {
"no-var": "off",
"no-console": "off" "no-console": "off"
} }
}, },

View File

@ -2,7 +2,7 @@
"use strict"; "use strict";
function byteArrayToHex(arrayBuffer){ function byteArrayToHex(arrayBuffer){
var chunks = []; const chunks = [];
(new Uint32Array(arrayBuffer)).forEach(function(num){ (new Uint32Array(arrayBuffer)).forEach(function(num){
chunks.push(num.toString(16)); chunks.push(num.toString(16));
}); });
@ -11,17 +11,17 @@
}).join(""); }).join("");
} }
var container = document.getElementById("test"); const container = document.getElementById("test");
var hashContainer = container.querySelector(".hashes"); const hashContainer = container.querySelector(".hashes");
var hashSets = Object.create(null); let hashSets = Object.create(null);
function createSet(set){ function createSet(set){
if (!hashSets[set]){ if (!hashSets[set]){
var setContainer = document.createElement("tbody"); const setContainer = document.createElement("tbody");
hashContainer.appendChild(setContainer); hashContainer.appendChild(setContainer);
var nameRow = document.createElement("tr"); const nameRow = document.createElement("tr");
setContainer.appendChild(nameRow); setContainer.appendChild(nameRow);
var nameContainer = document.createElement("th"); const nameContainer = document.createElement("th");
nameRow.appendChild(nameContainer); nameRow.appendChild(nameContainer);
nameContainer.colSpan = 2; nameContainer.colSpan = 2;
nameContainer.textContent = set; nameContainer.textContent = set;
@ -29,37 +29,34 @@
} }
} }
function displayData(data, set, title){ async function displayData(data, set, title){
createSet(set); 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; titleNode.textContent = title;
container.appendChild(titleNode); container.appendChild(titleNode);
var hashNode = document.createElement("td"); const hashNode = document.createElement("td");
hashNode.textContent = "calculating hash"; hashNode.textContent = "calculating hash";
container.appendChild(hashNode); 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); hashSets[set].appendChild(container);
const hash = await crypto.subtle.digest("SHA-256", data);
hashNode.textContent = byteArrayToHex(hash);
} }
function getAudioContext(frequency = 1e4){ function getAudioContext(frequency = 1e4){
var context = new window.OfflineAudioContext(2, 44100, 44100); const context = new window.OfflineAudioContext(2, 44100, 44100);
// Create oscillator // Create oscillator
var pxi_oscillator = context.createOscillator(); const pxi_oscillator = context.createOscillator();
pxi_oscillator.type = "triangle"; pxi_oscillator.type = "triangle";
pxi_oscillator.frequency.value = frequency; pxi_oscillator.frequency.value = frequency;
// Create and configure compressor // Create and configure compressor
var pxi_compressor = context.createDynamicsCompressor(); const pxi_compressor = context.createDynamicsCompressor();
pxi_compressor.threshold && (pxi_compressor.threshold.value = -50); pxi_compressor.threshold && (pxi_compressor.threshold.value = -50);
pxi_compressor.knee && (pxi_compressor.knee.value = 40); pxi_compressor.knee && (pxi_compressor.knee.value = 40);
pxi_compressor.ratio && (pxi_compressor.ratio.value = 12); pxi_compressor.ratio && (pxi_compressor.ratio.value = 12);
@ -77,28 +74,28 @@
} }
function createEmptyData(){ function createEmptyData(){
var emptyArray = new Float32Array(44100); const emptyArray = new Float32Array(44100);
displayData(emptyArray, "empty buffer", "no API involved"); displayData(emptyArray, "empty buffer", "no API involved");
var emptyContext = new OfflineAudioContext(1, 44100, 44100); const emptyContext = new OfflineAudioContext(1, 44100, 44100);
var emptyBuffer = emptyContext.createBuffer(1, 44100, 44100); const emptyBuffer = emptyContext.createBuffer(1, 44100, 44100);
var emptyCopy = new Float32Array(44100); const emptyCopy = new Float32Array(44100);
emptyBuffer.copyFromChannel(emptyCopy, 0); emptyBuffer.copyFromChannel(emptyCopy, 0);
displayData(emptyCopy, "empty buffer", "copyFromChannel - first"); displayData(emptyCopy, "empty buffer", "copyFromChannel - first");
var emptyData = emptyBuffer.getChannelData(0); const emptyData = emptyBuffer.getChannelData(0);
displayData(emptyData, "empty buffer", "getChannelData - first"); displayData(emptyData, "empty buffer", "getChannelData - first");
displayData(emptyBuffer.getChannelData(0), "empty buffer", "getChannelData - second"); displayData(emptyBuffer.getChannelData(0), "empty buffer", "getChannelData - second");
var emptyCopy2 = new Float32Array(44100); const emptyCopy2 = new Float32Array(44100);
emptyBuffer.copyFromChannel(emptyCopy2, 0); emptyBuffer.copyFromChannel(emptyCopy2, 0);
displayData(emptyCopy2, "empty buffer", "copyFromChannel - second"); displayData(emptyCopy2, "empty buffer", "copyFromChannel - second");
} }
function getIframeWindow(){ function getIframeWindow(){
var l = window.length; const l = window.length;
var iframe = document.createElement("iframe"); const iframe = document.createElement("iframe");
document.body.appendChild(iframe); document.body.appendChild(iframe);
const iframeWindow = window[l]; const iframeWindow = window[l];
document.body.removeChild(iframe); document.body.removeChild(iframe);
@ -107,43 +104,43 @@
function createHashData(frequency = 1e4){ function createHashData(frequency = 1e4){
var context = getAudioContext(frequency); const context = getAudioContext(frequency);
var setName = " (" + frequency + " Hz)"; const setName = " (" + frequency + " Hz)";
createSet(setName); createSet(setName);
// Start audio processing // Start audio processing
context.startRendering(); context.startRendering();
context.oncomplete = function(event){ 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); getIframeWindow().AudioBuffer.prototype.copyFromChannel.call(event.renderedBuffer, copyTestIframe, 0);
displayData(copyTestIframe, setName, "copyFromChannel - iframe"); displayData(copyTestIframe, setName, "copyFromChannel - iframe");
var chunkTest = new Float32Array(44100); const chunkTest = new Float32Array(44100);
var number = new Float32Array(100); const number = new Float32Array(100);
for (var chunkI = 0; chunkI < 44100; chunkI += number.length){ for (let chunkI = 0; chunkI < 44100; chunkI += number.length){
event.renderedBuffer.copyFromChannel(number, 0, chunkI); event.renderedBuffer.copyFromChannel(number, 0, chunkI);
chunkTest.set(number, chunkI); chunkTest.set(number, chunkI);
} }
displayData(chunkTest, setName, "copyFromChannel - chunks"); displayData(chunkTest, setName, "copyFromChannel - chunks");
var copyTest = new Float32Array(44100); const copyTest = new Float32Array(44100);
event.renderedBuffer.copyFromChannel(copyTest, 0); event.renderedBuffer.copyFromChannel(copyTest, 0);
displayData(copyTest, setName, "copyFromChannel - first"); displayData(copyTest, setName, "copyFromChannel - first");
var getTest = event.renderedBuffer.getChannelData(0); const getTest = event.renderedBuffer.getChannelData(0);
displayData(getTest, setName, "getChannelData - first"); displayData(getTest, setName, "getChannelData - first");
displayData(event.renderedBuffer.getChannelData(0), setName, "getChannelData - second readout"); displayData(event.renderedBuffer.getChannelData(0), setName, "getChannelData - second readout");
displayData(event.renderedBuffer.getChannelData(1), setName, "getChannelData - second channel"); displayData(event.renderedBuffer.getChannelData(1), setName, "getChannelData - second channel");
var copyTest2 = new Float32Array(44100); const copyTest2 = new Float32Array(44100);
event.renderedBuffer.copyFromChannel(copyTest2, 0); event.renderedBuffer.copyFromChannel(copyTest2, 0);
displayData(copyTest2, setName, "copyFromChannel - second"); displayData(copyTest2, setName, "copyFromChannel - second");
if (frequency === 1e4){ if (frequency === 1e4){
var sum = 0; let sum = 0;
for (var i = 4500; i < 5000; i += 1) { for (let i = 4500; i < 5000; i += 1) {
sum += Math.abs(getTest[i]); sum += Math.abs(getTest[i]);
} }
container.querySelector(".sum").textContent = sum; container.querySelector(".sum").textContent = sum;

View File

@ -5,9 +5,9 @@ function draw(canvas){
canvas.setAttribute("width", 220); canvas.setAttribute("width", 220);
canvas.setAttribute("height", 30); 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.textBaseline = "top";
ctx.font = "14px 'Arial'"; ctx.font = "14px 'Arial'";
ctx.textBaseline = "alphabetic"; ctx.textBaseline = "alphabetic";
@ -24,33 +24,31 @@ function test(window){
"use strict"; "use strict";
// create window canvas // create window canvas
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
// draw image in window canvas // draw image in window canvas
draw(canvas); draw(canvas);
return window.HTMLCanvasElement.prototype.toDataURL.call(canvas); return window.HTMLCanvasElement.prototype.toDataURL.call(canvas);
} }
function hash(string){ async function hash(string){
"use strict"; "use strict";
var buffer = new TextEncoder("utf-8").encode(string); const buffer = new TextEncoder("utf-8").encode(string);
return crypto.subtle.digest("SHA-256", buffer).then(function(hash){ const hash = await crypto.subtle.digest("SHA-256", buffer);
var chunks = []; const chunks = [];
(new Uint32Array(hash)).forEach(function(num){ (new Uint32Array(hash)).forEach(function(num){
chunks.push(num.toString(16)); chunks.push(num.toString(16));
});
return chunks.map(function(chunk){
return "0".repeat(8 - chunk.length) + chunk;
}).join("");
}); });
return chunks.map(function(chunk){
return "0".repeat(8 - chunk.length) + chunk;
}).join("");
} }
var addLine = function(){ const addLine = function(){
"use strict"; "use strict";
var output = document.getElementById("results"); const output = document.getElementById("results");
return function(text){ return function(text){
var line = document.createElement("div"); const line = document.createElement("div");
line.textContent = text; line.textContent = text;
output.appendChild(line); output.appendChild(line);
}; };
@ -59,13 +57,9 @@ var addLine = function(){
addLine("window name at start: " + window.name); addLine("window name at start: " + window.name);
window.name = "CanvasBlocker CSP test"; window.name = "CanvasBlocker CSP test";
addLine("window name after set: " + window.name); addLine("window name after set: " + window.name);
hash(test(window)).then(function(hash){ (async function(){
"use strict"; "use strict";
addLine("canvas hash: " + hash); const hashValue = await hash(test(window));
return; addLine("canvas hash: " + hashValue);
}).catch(function(error){ }());
"use strict";
addLine("error while creating canvas hash: " + error);
});

View File

@ -1,13 +1,13 @@
(function(){ (function(){
"use strict"; "use strict";
var iframeCode = atob( const iframeCode = atob(
document.getElementById("iframe").src.replace(/^.+base64,/, "") document.getElementById("iframe").src.replace(/^.+base64,/, "")
); );
document.getElementById("code").textContent = iframeCode; document.getElementById("code").textContent = iframeCode;
var blob = new Blob([iframeCode], {type: "text/html"}); const blob = new Blob([iframeCode], {type: "text/html"});
var newurl = window.URL.createObjectURL(blob); const newurl = window.URL.createObjectURL(blob);
document.getElementById("blobIframe").src = newurl; document.getElementById("blobIframe").src = newurl;
}()); }());

View File

@ -82,9 +82,9 @@
canvas.setAttribute("width", 220); canvas.setAttribute("width", 220);
canvas.setAttribute("height", 30); 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.textBaseline = "top";
ctx.font = "14px 'Arial'"; ctx.font = "14px 'Arial'";
ctx.textBaseline = "alphabetic"; ctx.textBaseline = "alphabetic";
@ -101,9 +101,9 @@
"use strict"; "use strict";
// create window canvas // create window canvas
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
// draw image in window canvas // draw image in window canvas
var ctx = draw(canvas); const ctx = draw(canvas);
return { return {
imageData: ctx.getImageData(0, 0, canvas.width, canvas.height), imageData: ctx.getImageData(0, 0, canvas.width, canvas.height),
url: canvas.toDataURL(), url: canvas.toDataURL(),
@ -125,7 +125,7 @@
function hashToString(hash){ function hashToString(hash){
"use strict"; "use strict";
var chunks = []; const chunks = [];
(new Uint32Array(hash)).forEach(function(num){ (new Uint32Array(hash)).forEach(function(num){
chunks.push(num.toString(16)); chunks.push(num.toString(16));
}); });
@ -134,38 +134,34 @@
}).join(""); }).join("");
} }
var send = function(){ const send = function(){
"use strict"; "use strict";
return function send(form, {url, imageData, isPointInPath}){ return async function send(form, {url, imageData, isPointInPath}){
var buffer = new TextEncoder("utf-8").encode(url); const buffer = new TextEncoder("utf-8").encode(url);
Promise.all([ const hashes = await Promise.all([
crypto.subtle.digest("SHA-256", buffer), crypto.subtle.digest("SHA-256", buffer),
crypto.subtle.digest("SHA-256", imageData.data) crypto.subtle.digest("SHA-256", imageData.data)
]).then(function(hashes){ ]);
var data = JSON.stringify({ const data = JSON.stringify({
urlHash: hashToString(hashes[0]), urlHash: hashToString(hashes[0]),
imageDataHash: hashToString(hashes[1]), imageDataHash: hashToString(hashes[1]),
isPointInPath isPointInPath
}, null, "\t"); }, null, "\t");
form.fingerprint.value = data; form.fingerprint.value = data;
var xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("POST", form.action + "?main", true); xhr.open("POST", form.action + "?main", true);
xhr.onreadystatechange = function(){ xhr.onreadystatechange = function(){
if (this.readyState === 4){ if (this.readyState === 4){
const status = this.status; const status = this.status;
if (status === 200 || status === 304) { if (status === 200 || status === 304) {
console.log("Sending xhr successful from main page:", data); console.log("Sending xhr successful from main page:", data);
}
else {
console.log("Sending xhr failed:", this);
}
} }
}; else {
xhr.send(new FormData(form)); console.log("Sending xhr failed:", this);
return; }
}).catch(function(error){ }
console.error(error); };
}); xhr.send(new FormData(form));
}; };
}(); }();

View File

@ -1,19 +1,19 @@
var addTest = (function(){ const addTest = (function(){
"use strict"; "use strict";
var statusDefinitions = [ const statusDefinitions = [
{className: "notRun", text: "not run"}, {className: "notRun", text: "not run"},
{className: "loud", text: "API tampering detected"}, {className: "loud", text: "API tampering detected"},
{className: "stealthy", text: "no API tampering detected"}, {className: "stealthy", text: "no API tampering detected"},
{className: "failed", text: "test failed"} {className: "failed", text: "test failed"}
]; ];
var ul = document.getElementById("tests"); const ul = document.getElementById("tests");
return function addTest(name, func){ return function addTest(name, func){
var logs = []; const logs = [];
function log(){ function log(){
logs.push(Array.prototype.slice.call(arguments).join(" ")); logs.push(Array.prototype.slice.call(arguments).join(" "));
} }
var status = 0; let status = 0;
try { try {
status = func(log)? 1: 2; status = func(log)? 1: 2;
} }
@ -21,15 +21,15 @@ var addTest = (function(){
console.log(error); console.log(error);
status = 3; status = 3;
} }
var li = document.createElement("li"); const li = document.createElement("li");
li.className = statusDefinitions[status].className; li.className = statusDefinitions[status].className;
var nameNode = document.createElement("span"); const nameNode = document.createElement("span");
nameNode.className = "name"; nameNode.className = "name";
nameNode.textContent = name; nameNode.textContent = name;
nameNode.title = func.toString(); nameNode.title = func.toString();
li.appendChild(nameNode); li.appendChild(nameNode);
li.appendChild(document.createTextNode(": ")); li.appendChild(document.createTextNode(": "));
var statusNode = document.createElement("span"); const statusNode = document.createElement("span");
statusNode.className = "status"; statusNode.className = "status";
statusNode.textContent = statusDefinitions[status].text; statusNode.textContent = statusDefinitions[status].text;
statusNode.title = logs.join("\n"); statusNode.title = logs.join("\n");
@ -41,15 +41,15 @@ var addTest = (function(){
function checkPropertyDescriptor(object, name, expectedDescriptor, log){ function checkPropertyDescriptor(object, name, expectedDescriptor, log){
"use strict"; "use strict";
var descriptor = Object.getOwnPropertyDescriptor(object, name); const descriptor = Object.getOwnPropertyDescriptor(object, name);
var detected = false; let detected = false;
function logProperty(desc, got, expected){ function logProperty(desc, got, expected){
log("Wrong", desc, "for", name, "- got:", got, "- expected: ", expected); log("Wrong", desc, "for", name, "- got:", got, "- expected: ", expected);
} }
function compare(desc, getter){ function compare(desc, getter){
var got = getter(descriptor); const got = getter(descriptor);
var expected = getter(expectedDescriptor); const expected = getter(expectedDescriptor);
if ((typeof expected) === "function"){ if ((typeof expected) === "function"){
if (got.name !== expected.name){ if (got.name !== expected.name){
@ -94,7 +94,7 @@ addTest("function length", function(log){
}); });
addTest("function code", function(log){ addTest("function code", function(log){
"use strict"; "use strict";
var codeDetected = false; let codeDetected = false;
function checkFunctionCode(func, expectedName){ function checkFunctionCode(func, expectedName){
log("checking", expectedName); log("checking", expectedName);
if (!func.toString().match( if (!func.toString().match(
@ -228,7 +228,6 @@ addTest("property descriptor", function(log){
object: CanvasRenderingContext2D.prototype, object: CanvasRenderingContext2D.prototype,
name: "getImageData", name: "getImageData",
descriptor: { descriptor: {
// eslint-disable-next-line no-unused-vars
value: function getImageData(x, y, w, h){}, value: function getImageData(x, y, w, h){},
writable: true, writable: true,
enumerable: true, enumerable: true,
@ -264,9 +263,9 @@ addTest("property descriptor", function(log){
addTest("error provocation 1", function(log){ addTest("error provocation 1", function(log){
"use strict"; "use strict";
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
var canvasBlocker = false; let canvasBlocker = false;
try{ try{
ctx.getImageData(0, 0, 0, 0); ctx.getImageData(0, 0, 0, 0);
} }
@ -284,10 +283,10 @@ addTest("error provocation 1", function(log){
addTest("error provocation 2", function(log){ addTest("error provocation 2", function(log){
"use strict"; "use strict";
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
canvas.width = 0; canvas.width = 0;
var ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
var canvasBlocker = false; let canvasBlocker = false;
try{ try{
ctx.getImageData(0, 0, 1, 1); ctx.getImageData(0, 0, 1, 1);
log("no error provoked"); log("no error provoked");
@ -306,7 +305,7 @@ addTest("error provocation 2", function(log){
addTest("error provocation 3", function(log){ addTest("error provocation 3", function(log){
"use strict"; "use strict";
var canvasBlocker = false; let canvasBlocker = false;
try{ try{
CanvasRenderingContext2D.prototype.getImageData.apply(undefined, [0, 0, 1, 1]); CanvasRenderingContext2D.prototype.getImageData.apply(undefined, [0, 0, 1, 1]);
} }
@ -324,23 +323,23 @@ addTest("error provocation 3", function(log){
addTest("error properties", function(log){ addTest("error properties", function(log){
"use strict"; "use strict";
var canvasBlocker = false; let canvasBlocker = false;
try{ try{
CanvasRenderingContext2D.prototype.getImageData.apply(undefined, [0, 0, 1, 1]); CanvasRenderingContext2D.prototype.getImageData.apply(undefined, [0, 0, 1, 1]);
} }
catch (error){ catch (error){
try { try {
var name = "TypeError"; const name = "TypeError";
if (error.name !== name && error instanceof TypeError){ if (error.name !== name && error instanceof TypeError){
log("Error name wrong. Expected: ", name, "- got:", error.name); log("Error name wrong. Expected: ", name, "- got:", error.name);
canvasBlocker = true; canvasBlocker = true;
} }
var start = "@" + location.href.replace(/\.html$/, ".js"); const start = "@" + location.href.replace(/\.html$/, ".js");
if (!error.stack.startsWith(start)){ if (!error.stack.startsWith(start)){
log("Error stack starts wrong. Expected:", start, "- got :", error.stack.split(/\n/g, 2)[0]); log("Error stack starts wrong. Expected:", start, "- got :", error.stack.split(/\n/g, 2)[0]);
canvasBlocker = true; canvasBlocker = true;
} }
var message = "'getImageData' called on an object that " + const message = "'getImageData' called on an object that " +
"does not implement interface CanvasRenderingContext2D."; "does not implement interface CanvasRenderingContext2D.";
if (error.message !== message){ if (error.message !== message){
log("Error message wrong. Expected: ", message, "- got:", error.message); log("Error message wrong. Expected: ", message, "- got:", error.message);
@ -356,12 +355,12 @@ addTest("error properties", function(log){
function testKnownPixelValue(size, log){ function testKnownPixelValue(size, log){
"use strict"; "use strict";
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
canvas.height = size; canvas.height = size;
canvas.width = size; canvas.width = size;
var context = canvas.getContext("2d"); const context = canvas.getContext("2d");
var imageData = new ImageData(canvas.width, canvas.height); const imageData = new ImageData(canvas.width, canvas.height);
var pixelValues = imageData.data; const pixelValues = imageData.data;
for (let i = 0; i < imageData.data.length; i += 1){ for (let i = 0; i < imageData.data.length; i += 1){
if (i % 4 !== 3){ if (i % 4 !== 3){
pixelValues[i] = Math.floor(256 * Math.random()); pixelValues[i] = Math.floor(256 * Math.random());
@ -371,8 +370,8 @@ function testKnownPixelValue(size, log){
} }
} }
context.putImageData(imageData, 0, 0); context.putImageData(imageData, 0, 0);
var p = context.getImageData(0, 0, canvas.width, canvas.height).data; const p = context.getImageData(0, 0, canvas.width, canvas.height).data;
for (var i = 0; i < p.length; i += 1){ for (let i = 0; i < p.length; i += 1){
if (p[i] !== pixelValues[i]){ if (p[i] !== pixelValues[i]){
log("wrong value", p[i], "at", i, "expected", pixelValues[i]); log("wrong value", p[i], "at", i, "expected", pixelValues[i]);
return true; return true;
@ -393,9 +392,9 @@ addTest("known pixel value test 10", function(log){
addTest("double readout test", function(log){ addTest("double readout test", function(log){
"use strict"; "use strict";
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
var context = canvas.getContext("2d"); const context = canvas.getContext("2d");
var imageData = context.getImageData(0, 0, canvas.width, canvas.height); const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
for (let i = 0; i < imageData.data.length; i += 1){ for (let i = 0; i < imageData.data.length; i += 1){
if (i % 4 !== 3){ if (i % 4 !== 3){
imageData.data[i] = Math.floor(256 * Math.random()); imageData.data[i] = Math.floor(256 * Math.random());
@ -406,8 +405,8 @@ addTest("double readout test", function(log){
} }
context.putImageData(imageData, 0, 0); context.putImageData(imageData, 0, 0);
var imageData1 = context.getImageData(0, 0, canvas.width, canvas.height); const imageData1 = context.getImageData(0, 0, canvas.width, canvas.height);
var imageData2 = 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){ for (let i = 0; i < imageData2.data.length; i += 1){
if (imageData1.data[i] !== imageData2.data[i]){ if (imageData1.data[i] !== imageData2.data[i]){
log("mismatch at", i, ":", log("mismatch at", i, ":",
@ -424,9 +423,9 @@ addTest("double readout test", function(log){
addTest("double readout test (toDataURL)", function(log){ addTest("double readout test (toDataURL)", function(log){
"use strict"; "use strict";
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
var context = canvas.getContext("2d"); const context = canvas.getContext("2d");
var imageData = context.getImageData(0, 0, canvas.width, canvas.height); const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
for (let i = 0; i < imageData.data.length; i += 1){ for (let i = 0; i < imageData.data.length; i += 1){
if (i % 4 !== 3){ if (i % 4 !== 3){
imageData.data[i] = Math.floor(256 * Math.random()); imageData.data[i] = Math.floor(256 * Math.random());
@ -437,8 +436,8 @@ addTest("double readout test (toDataURL)", function(log){
} }
context.putImageData(imageData, 0, 0); context.putImageData(imageData, 0, 0);
var dataURL1 = canvas.toDataURL(); const dataURL1 = canvas.toDataURL();
var dataURL2 = canvas.toDataURL(); const dataURL2 = canvas.toDataURL();
if (dataURL1 !== dataURL2){ if (dataURL1 !== dataURL2){
log("data URL missmatch:", log("data URL missmatch:",
dataURL1, dataURL1,
@ -452,9 +451,9 @@ addTest("double readout test (toDataURL)", function(log){
addTest("readout - in - out test", function(log){ addTest("readout - in - out test", function(log){
"use strict"; "use strict";
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
var context = canvas.getContext("2d"); const context = canvas.getContext("2d");
var imageData = context.getImageData(0, 0, canvas.width, canvas.height); const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
for (let i = 0; i < imageData.data.length; i += 1){ for (let i = 0; i < imageData.data.length; i += 1){
if (i % 4 !== 3){ if (i % 4 !== 3){
imageData.data[i] = Math.floor(256 * Math.random()); imageData.data[i] = Math.floor(256 * Math.random());
@ -465,11 +464,11 @@ addTest("readout - in - out test", function(log){
} }
context.putImageData(imageData, 0, 0); context.putImageData(imageData, 0, 0);
var imageData1 = context.getImageData(0, 0, canvas.width, canvas.height); const imageData1 = context.getImageData(0, 0, canvas.width, canvas.height);
var canvas2 = document.createElement("canvas"); const canvas2 = document.createElement("canvas");
var context2 = canvas2.getContext("2d"); const context2 = canvas2.getContext("2d");
context2.putImageData(imageData1, 0, 0); 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){ for (let i = 0; i < imageData2.data.length; i += 1){
if (imageData1.data[i] !== imageData2.data[i]){ if (imageData1.data[i] !== imageData2.data[i]){
log("mismatch at", i, ":", log("mismatch at", i, ":",
@ -486,9 +485,9 @@ addTest("readout - in - out test", function(log){
addTest("window name change", function(log){ addTest("window name change", function(log){
"use strict"; "use strict";
var oldName = window.name; const oldName = window.name;
log("old name:", oldName); log("old name:", oldName);
var newName = oldName + " added"; const newName = oldName + " added";
log("new name:", newName); log("new name:", newName);
window.name = newName; window.name = newName;
@ -502,7 +501,7 @@ addTest("window name change", function(log){
function checkDOMRectData(rect, data, log){ function checkDOMRectData(rect, data, log){
"use strict"; "use strict";
var detected = false; let detected = false;
["x", "y", "width", "height"].forEach(function(property){ ["x", "y", "width", "height"].forEach(function(property){
if (data[property] !== rect[property]){ if (data[property] !== rect[property]){
log("Wrong value for", property, ":", 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){ function getRectByData(data){
"use strict"; "use strict";
var el = document.createElement("div"); const el = document.createElement("div");
el.style.cssText = "position: fixed;" + el.style.cssText = "position: fixed;" +
"left: " + data.x + "px; " + "left: " + data.x + "px; " +
"top: " + data.y + "px; " + "top: " + data.y + "px; " +
@ -523,7 +522,7 @@ function getRectByData(data){
"height: " + data.height + "px;"; "height: " + data.height + "px;";
document.body.appendChild(el); document.body.appendChild(el);
var rect = el.getBoundingClientRect(); const rect = el.getBoundingClientRect();
document.body.removeChild(el); document.body.removeChild(el);
return rect; return rect;
} }
@ -531,41 +530,41 @@ function getRectByData(data){
addTest("self created DOMRect", function(log){ addTest("self created DOMRect", function(log){
"use strict"; "use strict";
var data = { const data = {
x: Math.PI, x: Math.PI,
y: Math.E, y: Math.E,
width: Math.LOG10E, width: Math.LOG10E,
height: Math.LOG2E 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); return checkDOMRectData(rect, data, log);
}); });
addTest("known DOMRect", function(log){ addTest("known DOMRect", function(log){
"use strict"; "use strict";
var data = { const data = {
x: 1 + 1/4, x: 1 + 1/4,
y: 2, y: 2,
width: 3, width: 3,
height: 4 height: 4
}; };
var rect = getRectByData(data); const rect = getRectByData(data);
return checkDOMRectData(rect, data, log); return checkDOMRectData(rect, data, log);
}); });
addTest("changed DOMRect", function(log){ addTest("changed DOMRect", function(log){
"use strict"; "use strict";
var data = { const data = {
x: Math.PI, x: Math.PI,
y: 2, y: 2,
width: 3, width: 3,
height: 4 height: 4
}; };
var rect = getRectByData(data); const rect = getRectByData(data);
rect.x = Math.PI; rect.x = Math.PI;
return checkDOMRectData(rect, data, log); return checkDOMRectData(rect, data, log);
@ -573,15 +572,15 @@ addTest("changed DOMRect", function(log){
addTest("recreated DOMRect", function(log){ addTest("recreated DOMRect", function(log){
"use strict"; "use strict";
var data = { const data = {
x: Math.PI, x: Math.PI,
y: Math.E, y: Math.E,
width: Math.LOG10E, width: Math.LOG10E,
height: Math.LOG2E height: Math.LOG2E
}; };
var rect = getRectByData(data); const rect = getRectByData(data);
var rect2 = getRectByData(rect); const rect2 = getRectByData(rect);
return checkDOMRectData(rect2, rect, log); return checkDOMRectData(rect2, rect, log);
}); });

View File

@ -1,7 +1,7 @@
(function(){ (function(){
"use strict"; "use strict";
function byteArrayToHex(arrayBuffer){ function byteArrayToHex(arrayBuffer){
var chunks = []; const chunks = [];
(new Uint32Array(arrayBuffer)).forEach(function(num){ (new Uint32Array(arrayBuffer)).forEach(function(num){
chunks.push(num.toString(16)); chunks.push(num.toString(16));
}); });
@ -28,7 +28,7 @@
} }
const properties = ["x", "y", "width", "height", "top", "left", "right", "bottom"]; 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){ const rects = getElements().map(function(element){
return { return {
name: element.dataset.name, name: element.dataset.name,
@ -42,15 +42,10 @@
}); });
}); });
crypto.subtle.digest("SHA-256", data) const hash = await crypto.subtle.digest("SHA-256", data);
.then(function(hash){ output.querySelector(".hash").textContent = byteArrayToHex(hash);
output.querySelector(".hash").textContent = byteArrayToHex(hash);
return;
}).catch(function(error){
output.querySelector(".hash").textContent = "Unable to compute hash: " + error;
});
var dataNode = output.querySelector(".data"); const dataNode = output.querySelector(".data");
dataNode.innerHTML = "<table><tr><th></th>" + dataNode.innerHTML = "<table><tr><th></th>" +
rects.map(function(rect){ rects.map(function(rect){
return "<th>" + rect.name + "</th>"; return "<th>" + rect.name + "</th>";
@ -68,22 +63,16 @@
}).join("") + "</tr>"; }).join("") + "</tr>";
}).join("") + }).join("") +
"</table>"; "</table>";
rects.forEach(function(rect){ rects.forEach(async function(rect){
const data = new Float64Array(properties.length); const data = new Float64Array(properties.length);
properties.forEach(function(property, i){ properties.forEach(function(property, i){
data[i] = rect.data[property]; data[i] = rect.data[property];
}); });
crypto.subtle.digest("SHA-256", data).then(function(hash){ const hash = await crypto.subtle.digest("SHA-256", data);
dataNode.querySelector( dataNode.querySelector(
".rectHash[data-name=\"" + rect.name + "\"]" ".rectHash[data-name=\"" + rect.name + "\"]"
).textContent = byteArrayToHex(hash); ).textContent = byteArrayToHex(hash);
return;
}).catch(function(error){
dataNode.querySelector(
".rectHash[data-name=\"" + rect.name + "\"]"
).textContent = "Unable to compute hash: " + error;
});
}); });
} }
@ -144,12 +133,12 @@
return element.getBoundingClientRect(); return element.getBoundingClientRect();
}); });
createTest("Range.getClientRects", function(element){ createTest("Range.getClientRects", function(element){
var range = document.createRange(); const range = document.createRange();
range.selectNode(element); range.selectNode(element);
return range.getClientRects()[0]; return range.getClientRects()[0];
}); });
createTest("Range.getBoundingClientRect", function(element){ createTest("Range.getBoundingClientRect", function(element){
var range = document.createRange(); const range = document.createRange();
range.selectNode(element); range.selectNode(element);
return range.getBoundingClientRect(); return range.getBoundingClientRect();
}); });

View File

@ -90,11 +90,11 @@
document.body.innerHTML = "<iframe></iframe>"; document.body.innerHTML = "<iframe></iframe>";
log("TEST:", "innerHTML after 1000ms:", compare(test(window[0]), reference)); log("TEST:", "innerHTML after 1000ms:", compare(test(window[0]), reference));
var iFrame = document.createElement("iframe"); const iFrame = document.createElement("iframe");
document.body.appendChild(iFrame); document.body.appendChild(iFrame);
log("TEST:", "appendChild after 1000ms:", compare(test(window[1]), reference)); log("TEST:", "appendChild after 1000ms:", compare(test(window[1]), reference));
var iFrame2 = document.createElement("iframe"); const iFrame2 = document.createElement("iframe");
iFrame.replaceWith(iFrame2); iFrame.replaceWith(iFrame2);
log("TEST:", "replaceWith after 1000ms:", compare(test(window[1]), reference)); 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 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>" + "<li>all lines with \"TEST:\" should have a \"match\" at the end</li>" +
"</ul>"; "</ul>";
var title = document.createElement("title"); const title = document.createElement("title");
title.textContent = "iFrame test"; title.textContent = "iFrame test";
document.getElementsByTagName("head")[0].appendChild(title); document.getElementsByTagName("head")[0].appendChild(title);
}, 1000); }, 1000);

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line no-var
var log = function(){ var log = function(){
"use strict"; "use strict";
return function log(...str){ return function log(...str){
@ -18,9 +19,9 @@ function draw(canvas){
canvas.setAttribute("width", 220); canvas.setAttribute("width", 220);
canvas.setAttribute("height", 30); 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.textBaseline = "top";
ctx.font = "14px 'Arial'"; ctx.font = "14px 'Arial'";
ctx.textBaseline = "alphabetic"; ctx.textBaseline = "alphabetic";
@ -37,39 +38,34 @@ function test(window){
"use strict"; "use strict";
// create window canvas // create window canvas
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
// draw image in window canvas // draw image in window canvas
draw(canvas); draw(canvas);
return window.HTMLCanvasElement.prototype.toDataURL.call(canvas); return window.HTMLCanvasElement.prototype.toDataURL.call(canvas);
} }
function hash(string){ async function hash(string){
"use strict"; "use strict";
var buffer = new TextEncoder("utf-8").encode(string); const buffer = new TextEncoder("utf-8").encode(string);
return crypto.subtle.digest("SHA-256", buffer).then(function(hash){ const hash = await crypto.subtle.digest("SHA-256", buffer);
var chunks = []; const chunks = [];
(new Uint32Array(hash)).forEach(function(num){ (new Uint32Array(hash)).forEach(function(num){
chunks.push(num.toString(16)); chunks.push(num.toString(16));
});
return chunks.map(function(chunk){
return "0".repeat(8 - chunk.length) + chunk;
}).join("");
}); });
return chunks.map(function(chunk){
return "0".repeat(8 - chunk.length) + chunk;
}).join("");
} }
function compare(string1, string2, alwaysOutputHashes){ function compare(string1, string2, alwaysOutputHashes){
"use strict"; "use strict";
function outputHashes(message){ async function outputHashes(message){
return Promise.all([ const hashes = await Promise.all([
hash(string1), hash(string1),
hash(string2) hash(string2)
]).then(function(hashes){ ]);
console.log(message, ...hashes); console.log(message, ...hashes);
return;
});
} }
if (string1 === string2){ if (string1 === string2){
@ -100,14 +96,15 @@ function compare(string1, string2, alwaysOutputHashes){
} }
} }
} }
// eslint-disable-next-line no-var
var reference = test(window); var reference = test(window);
hash(reference).then(function(hash){ (async function(){
"use strict"; "use strict";
try {
log("reference hash:", hash); const hashValue = await hash(reference);
return; log("reference hash:", hashValue);
}).catch(function(error){ }
"use strict"; catch (error){
log("%cX", "color: red", "Unable to compute reference hash:", error);
log("%cX", "color: red", "Unable to compute reference hash:", error); }
}); }());

View File

@ -1,15 +1,15 @@
var createLog = function(){ const createLog = function(){
"use strict"; "use strict";
var div = document.getElementById("log"); const div = document.getElementById("log");
return function createLog(){ return function createLog(){
var logDiv = document.createElement("div"); const logDiv = document.createElement("div");
logDiv.className = "log"; logDiv.className = "log";
div.appendChild(logDiv); div.appendChild(logDiv);
return function createLine(str){ return function createLine(str){
var logLine = document.createElement("div"); const logLine = document.createElement("div");
logLine.className = "logLine"; logLine.className = "logLine";
logDiv.appendChild(logLine); logDiv.appendChild(logLine);
logLine.textContent = str; logLine.textContent = str;
@ -20,7 +20,7 @@ var createLog = function(){
}; };
}(); }();
var log = createLog(); let log = createLog();
log("user agent equal between server and client: " + ( log("user agent equal between server and client: " + (
document.getElementById("serverUserAgent").text === navigator.userAgent 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){ Object.keys(navigator.__proto__).sort().forEach(function(property){
"use strict"; "use strict";
var value = navigator[property]; const value = navigator[property];
if ((typeof value) === "string"){ if ((typeof value) === "string"){
log(property + ": " + value); log(property + ": " + value);
} }
}); });
var section = document.createElement("h2"); const section = document.createElement("h2");
section.textContent = "Values in iFrame"; section.textContent = "Values in iFrame";
document.getElementById("log").append(section); document.getElementById("log").append(section);
log = createLog(); log = createLog();
var iframe = document.createElement("iframe"); const iframe = document.createElement("iframe");
document.body.appendChild(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){ Object.keys(navigator.__proto__).sort().forEach(function(property){
"use strict"; "use strict";
var value = iframeWindow.navigator[property]; const value = iframeWindow.navigator[property];
if ((typeof value) === "string"){ if ((typeof value) === "string"){
log(property + "@iframe: " + value); log(property + "@iframe: " + value);
} }

View File

@ -1,23 +1,23 @@
var createLog = function(){ const createLog = function(){
"use strict"; "use strict";
var div = document.getElementById("log"); const div = document.getElementById("log");
return function createLog(){ return function createLog(){
var logDiv = document.createElement("div"); const logDiv = document.createElement("div");
logDiv.className = "log"; logDiv.className = "log";
div.appendChild(logDiv); div.appendChild(logDiv);
return { return {
createButton: function createButton(text, callback){ createButton: function createButton(text, callback){
var button = document.createElement("button"); const button = document.createElement("button");
button.className = "logButton"; button.className = "logButton";
logDiv.appendChild(button); logDiv.appendChild(button);
button.textContent = text; button.textContent = text;
button.addEventListener("click", callback); button.addEventListener("click", callback);
}, },
createLine: function createLine(str, type = "div"){ createLine: function createLine(str, type = "div"){
var logLine = document.createElement(type); const logLine = document.createElement(type);
logLine.className = "logLine"; logLine.className = "logLine";
logDiv.appendChild(logLine); logDiv.appendChild(logLine);
logLine.textContent = str; logLine.textContent = str;
@ -29,20 +29,20 @@ var createLog = function(){
}; };
}(); }();
var performTest = function(){ const performTest = function(){
"use strict"; "use strict";
return function performTest(name, func, innerRunLength, outerRunLength){ return function performTest(name, func, innerRunLength, outerRunLength){
var log = createLog(); const log = createLog();
log.createLine("test " + name, "h3"); log.createLine("test " + name, "h3");
var line = log.createLine(""); const line = log.createLine("");
var line2; let line2;
var time = 0; let time = 0;
var time2 = 0; let time2 = 0;
var min = Number.POSITIVE_INFINITY; let min = Number.POSITIVE_INFINITY;
var max = 0; let max = 0;
var outerI = 0; let outerI = 0;
var outerRunIncrease = outerRunLength; const outerRunIncrease = outerRunLength;
if (func.prepareOnce){ if (func.prepareOnce){
func.prepareOnce(); func.prepareOnce();
} }
@ -50,21 +50,21 @@ var performTest = function(){
line("starting"); line("starting");
line2(""); line2("");
function run(){ function run(){
for (var i = 0; i < innerRunLength; i += 1){ for (let i = 0; i < innerRunLength; i += 1){
if (func.prepare){ if (func.prepare){
func.prepare(); func.prepare();
} }
var start = performance.now(); const start = performance.now();
func.test(); func.test();
var end = performance.now(); const end = performance.now();
var duration = end - start; const duration = end - start;
min = Math.min(min, duration); min = Math.min(min, duration);
max = Math.max(max, duration); max = Math.max(max, duration);
time2 += duration * duration; time2 += duration * duration;
time += duration; time += duration;
} }
outerI += 1; outerI += 1;
var totalRunI = outerI * innerRunLength; const totalRunI = outerI * innerRunLength;
line( line(
"finished run " + totalRunI + " from " + (innerRunLength * outerRunLength) + "finished run " + totalRunI + " from " + (innerRunLength * outerRunLength) +
" -> average: " + (time / totalRunI).toFixed(2) + " -> average: " + (time / totalRunI).toFixed(2) +
@ -91,9 +91,9 @@ function draw(canvas){
canvas.setAttribute("width", 220); canvas.setAttribute("width", 220);
canvas.setAttribute("height", 30); 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.textBaseline = "top";
ctx.font = "14px 'Arial'"; ctx.font = "14px 'Arial'";
ctx.textBaseline = "alphabetic"; ctx.textBaseline = "alphabetic";
@ -107,10 +107,10 @@ function draw(canvas){
return ctx; return ctx;
} }
var fingerprintTest = function(){ const fingerprintTest = function(){
"use strict"; "use strict";
var canvas; let canvas;
return { return {
prepare: function(){ prepare: function(){
// create window canvas // create window canvas
@ -124,20 +124,20 @@ var fingerprintTest = function(){
}; };
}(); }();
var randomImageTest = function(){ const randomImageTest = function(){
"use strict"; "use strict";
var canvas; let canvas;
return { return {
prepare: function(){ prepare: function(){
canvas = document.createElement("canvas"); canvas = document.createElement("canvas");
canvas.width = 1000; canvas.width = 1000;
canvas.height = 100; canvas.height = 100;
var imageData = new ImageData(canvas.width, canvas.height); const imageData = new ImageData(canvas.width, canvas.height);
var data = imageData.data; 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 + 0] = Math.floor(256 * Math.random());
data[i + 1] = Math.floor(256 * Math.random()); data[i + 1] = Math.floor(256 * Math.random());
data[i + 2] = 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"; "use strict";
var div; let div;
return { return {
prepareOnce: function(){ prepareOnce: function(){
@ -164,7 +164,7 @@ var innerHTMlTest = function(html, repeats){
document.body.appendChild(div); document.body.appendChild(div);
}, },
test: function randomImageTest(){ test: function randomImageTest(){
for (var i = repeats; i--;){ for (let i = repeats; i--;){
div.innerHTML = html; div.innerHTML = html;
div.innerHTML = ""; div.innerHTML = "";
} }

View File

@ -1,14 +1,14 @@
function addTest(container, title, callback){ function addTest(container, title, callback){
"use strict"; "use strict";
var testContainer = document.createElement("div"); const testContainer = document.createElement("div");
testContainer.className = "test"; testContainer.className = "test";
var titleNode = document.createElement("h3"); const titleNode = document.createElement("h3");
titleNode.textContent = title; titleNode.textContent = title;
testContainer.appendChild(titleNode); testContainer.appendChild(titleNode);
var resultsNode = document.createElement("div"); const resultsNode = document.createElement("div");
resultsNode.className = "results"; resultsNode.className = "results";
testContainer.appendChild(resultsNode); testContainer.appendChild(resultsNode);
@ -22,27 +22,28 @@ function addConsistencyTest(title, callback){
addTest(document.getElementById("consistency"), title, function(resultsNode){ addTest(document.getElementById("consistency"), title, function(resultsNode){
var line = document.createElement("div"); const line = document.createElement("div");
line.textContent = "consistent: "; line.textContent = "consistent: ";
resultsNode.appendChild(line); resultsNode.appendChild(line);
var consistent = document.createElement("span"); const consistent = document.createElement("span");
consistent.className = "result"; consistent.className = "result";
line.appendChild(consistent); line.appendChild(consistent);
function compute(){ async function compute(){
consistent.textContent = "computing"; consistent.textContent = "computing";
callback().then(function(value){ try {
const value = await callback();
consistent.textContent = value? "OK": "not OK"; consistent.textContent = value? "OK": "not OK";
return; }
}).catch(function(error){ catch (error){
consistent.classList.add("failed"); consistent.classList.add("failed");
if (Array.isArray(error)){ if (Array.isArray(error)){
consistent.textContent = ""; consistent.textContent = "";
var ul = document.createElement("ul"); const ul = document.createElement("ul");
consistent.appendChild(ul); consistent.appendChild(ul);
error.forEach(function(error){ error.forEach(function(error){
var li = document.createElement("li"); const li = document.createElement("li");
li.textContent = error; li.textContent = error;
ul.appendChild(li); ul.appendChild(li);
}); });
@ -50,7 +51,7 @@ function addConsistencyTest(title, callback){
else { else {
consistent.textContent = error; consistent.textContent = error;
} }
}); }
} }
compute(); compute();
window.addEventListener("resize", compute); window.addEventListener("resize", compute);
@ -76,7 +77,7 @@ addConsistencyTest("screen properties", function(){
addConsistencyTest("media queries: window.matchMedia - low value", function(){ addConsistencyTest("media queries: window.matchMedia - low value", function(){
"use strict"; "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( return Promise.resolve(
window.matchMedia("(min-device-width: " + value + "px)").matches && 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(){ addConsistencyTest("media queries: window.matchMedia - reported value", function(){
"use strict"; "use strict";
var errors = []; const errors = [];
["width", "height"].forEach(function(dimension){ ["width", "height"].forEach(function(dimension){
["min-", "max-", ""].forEach(function(comparison){ ["min-", "max-", ""].forEach(function(comparison){
var queryBase = "(" + comparison + "device-" + dimension + ": "; const queryBase = "(" + comparison + "device-" + dimension + ": ";
var query = queryBase + screen[dimension] + "px)"; let query = queryBase + screen[dimension] + "px)";
if (!window.matchMedia(query).matches){ if (!window.matchMedia(query).matches){
errors.push(query + " did not match."); errors.push(query + " did not match.");
query = queryBase + (screen[dimension] + 1) + "px)"; 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(){ addConsistencyTest("media queries: window.matchMedia - big value", function(){
"use strict"; "use strict";
var value = Math.max(screen.width, screen.height) * 2; const value = Math.max(screen.width, screen.height) * 2;
return Promise.resolve( return Promise.resolve(
!window.matchMedia("(min-device-width: " + value + "px)").matches && !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"; "use strict";
return function addResolutionTest(title, callback, properties = ["width", "height"]){ return function addResolutionTest(title, callback, properties = ["width", "height"]){
addTest(document.getElementById("resolution"), title, function(resultsNode){ addTest(document.getElementById("resolution"), title, function(resultsNode){
properties.forEach(function(type){ properties.forEach(function(type){
var line = document.createElement("div"); const line = document.createElement("div");
line.textContent = type + ": "; line.textContent = type + ": ";
resultsNode.appendChild(line); resultsNode.appendChild(line);
var number = document.createElement("span"); const number = document.createElement("span");
number.className = "result " + type; number.className = "result " + type;
line.appendChild(number); line.appendChild(number);
function compute(){ async function compute(){
number.textContent = "computing"; number.textContent = "computing";
callback(type).then(function(value){ try {
const value = await callback(type);
number.textContent = value; number.textContent = value;
return; }
}).catch(function(error){ catch (error){
number.classList.add("failed"); number.classList.add("failed");
number.textContent = error; number.textContent = error;
}); }
} }
compute(); compute();
window.addEventListener("resize", compute); window.addEventListener("resize", compute);
@ -185,78 +187,76 @@ addResolutionTest("window properties: outer...", function(type){
]); ]);
}); });
function searchValue(tester){ async function searchValue(tester){
"use strict"; "use strict";
var minValue = 0; let minValue = 0;
var maxValue = 512; let maxValue = 512;
var ceiling = Math.pow(2, 32); const ceiling = Math.pow(2, 32);
function stepUp(){ async function stepUp(){
if (maxValue > ceiling){ if (maxValue > ceiling){
return Promise.reject("Unable to find upper bound"); return Promise.reject("Unable to find upper bound");
} }
return tester(maxValue).then(function(testResult){ const testResult = await tester(maxValue);
if (testResult === searchValue.isEqual){ if (testResult === searchValue.isEqual){
return maxValue; return maxValue;
} }
else if (testResult === searchValue.isBigger){ else if (testResult === searchValue.isBigger){
minValue = maxValue; minValue = maxValue;
maxValue *= 2; maxValue *= 2;
return stepUp(); 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 { else {
return false; const testResult = await tester(maxValue);
}
});
}
function binarySearch(){
if (maxValue - minValue < 0.01){
return tester(minValue).then(function(testResult){
if (testResult.isEqual){ if (testResult.isEqual){
return minValue; return maxValue;
} }
else { else {
// eslint-disable-next-line promise/no-nesting throw "Search could not find exact value." +
return tester(maxValue).then(function(testResult){ " It's between " + minValue + " and " + maxValue + ".";
if (testResult.isEqual){
return maxValue;
}
else {
throw "Search could not find exact value." +
" It's between " + minValue + " and " + maxValue + ".";
}
});
} }
}); }
} }
else { else {
var pivot = (minValue + maxValue) / 2; const pivot = (minValue + maxValue) / 2;
return tester(pivot).then(function(testResult){ const testResult = await tester(pivot);
if (testResult === searchValue.isEqual){ if (testResult === searchValue.isEqual){
return pivot; return pivot;
} }
else if (testResult === searchValue.isBigger){ else if (testResult === searchValue.isBigger){
minValue = pivot; minValue = pivot;
return binarySearch(); return binarySearch();
} }
else { else {
maxValue = pivot; maxValue = pivot;
return binarySearch(); return binarySearch();
} }
});
} }
} }
return stepUp().then(function(stepUpResult){ const stepUpResult = await stepUp();
if (stepUpResult){ if (stepUpResult){
return stepUpResult; return stepUpResult;
} }
else { else {
return binarySearch(); return binarySearch();
} }
});
} }
searchValue.isSmaller = -1; searchValue.isSmaller = -1;
searchValue.isEqual = 0; searchValue.isEqual = 0;
@ -297,16 +297,16 @@ addResolutionTest("media queries: window.matchMedia (min)", function(type){
addResolutionTest("media queries: css (max)", function(type){ addResolutionTest("media queries: css (max)", function(type){
"use strict"; "use strict";
var tester = document.createElement("div"); const tester = document.createElement("div");
var id = "tester_" + (Math.random() * Math.pow(2, 32)).toString(36).replace(".", "_"); const id = "tester_" + (Math.random() * Math.pow(2, 32)).toString(36).replace(".", "_");
tester.id = id; tester.id = id;
document.body.appendChild(tester); document.body.appendChild(tester);
var style = document.createElement("style"); const style = document.createElement("style");
style.type = "text/css"; style.type = "text/css";
document.head.appendChild(style); 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;}"); styleSheet.insertRule("#" + id + "{position: fixed; right: 100%; z-index: 0;}");
return searchValue(function(valueToTest){ return searchValue(function(valueToTest){
@ -314,12 +314,12 @@ addResolutionTest("media queries: css (max)", function(type){
while (styleSheet.rules.length > 1){ while (styleSheet.rules.length > 1){
styleSheet.removeRule(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); styleSheet.insertRule(rule, 1);
rule = "@media (device-" + type + ": " + valueToTest + "px){#" + id + "{z-index: 2;}}"; rule = "@media (device-" + type + ": " + valueToTest + "px){#" + id + "{z-index: 2;}}";
styleSheet.insertRule(rule, 2); styleSheet.insertRule(rule, 2);
window.setTimeout(function(){ window.setTimeout(function(){
var testValue = window.getComputedStyle(tester).zIndex; const testValue = window.getComputedStyle(tester).zIndex;
switch (testValue){ switch (testValue){
case "0": case "0":
resolve(searchValue.isBigger); resolve(searchValue.isBigger);
@ -341,16 +341,16 @@ addResolutionTest("media queries: css (max)", function(type){
addResolutionTest("media queries: css (min)", function(type){ addResolutionTest("media queries: css (min)", function(type){
"use strict"; "use strict";
var tester = document.createElement("div"); const tester = document.createElement("div");
var id = "tester_" + (Math.random() * Math.pow(2, 32)).toString(36).replace(".", "_"); const id = "tester_" + (Math.random() * Math.pow(2, 32)).toString(36).replace(".", "_");
tester.id = id; tester.id = id;
document.body.appendChild(tester); document.body.appendChild(tester);
var style = document.createElement("style"); const style = document.createElement("style");
style.type = "text/css"; style.type = "text/css";
document.head.appendChild(style); 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;}"); styleSheet.insertRule("#" + id + "{position: fixed; right: 100%; z-index: 0;}");
return searchValue(function(valueToTest){ return searchValue(function(valueToTest){
@ -358,12 +358,12 @@ addResolutionTest("media queries: css (min)", function(type){
while (styleSheet.rules.length > 1){ while (styleSheet.rules.length > 1){
styleSheet.removeRule(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); styleSheet.insertRule(rule, 1);
rule = "@media (device-" + type + ": " + valueToTest + "px){#" + id + "{z-index: 2;}}"; rule = "@media (device-" + type + ": " + valueToTest + "px){#" + id + "{z-index: 2;}}";
styleSheet.insertRule(rule, 2); styleSheet.insertRule(rule, 2);
window.setTimeout(function(){ window.setTimeout(function(){
var testValue = window.getComputedStyle(tester).zIndex; const testValue = window.getComputedStyle(tester).zIndex;
switch (testValue){ switch (testValue){
case "0": case "0":
resolve(searchValue.isSmaller); resolve(searchValue.isSmaller);

View File

@ -14,9 +14,9 @@
canvas.setAttribute("width", 220); canvas.setAttribute("width", 220);
canvas.setAttribute("height", 30); 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.textBaseline = "top";
ctx.font = "14px 'Arial'"; ctx.font = "14px 'Arial'";
ctx.textBaseline = "alphabetic"; ctx.textBaseline = "alphabetic";
@ -33,9 +33,9 @@
"use strict"; "use strict";
// create window canvas // create window canvas
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
// draw image in window canvas // draw image in window canvas
var ctx = draw(canvas); const ctx = draw(canvas);
return { return {
imageData: ctx.getImageData(0, 0, canvas.width, canvas.height), imageData: ctx.getImageData(0, 0, canvas.width, canvas.height),
url: canvas.toDataURL(), url: canvas.toDataURL(),
@ -56,7 +56,7 @@
function hashToString(hash){ function hashToString(hash){
"use strict"; "use strict";
var chunks = []; const chunks = [];
(new Uint32Array(hash)).forEach(function(num){ (new Uint32Array(hash)).forEach(function(num){
chunks.push(num.toString(16)); chunks.push(num.toString(16));
}); });
@ -65,47 +65,45 @@
}).join(""); }).join("");
} }
var send = function(){ const send = function(){
"use strict"; "use strict";
return function send(form, {url, imageData, isPointInPath}){ return async function send(form, {url, imageData, isPointInPath}){
var buffer = new TextEncoder("utf-8").encode(url); const buffer = new TextEncoder("utf-8").encode(url);
return Promise.all([ const hashes = await Promise.all([
crypto.subtle.digest("SHA-256", buffer), crypto.subtle.digest("SHA-256", buffer),
crypto.subtle.digest("SHA-256", imageData.data) crypto.subtle.digest("SHA-256", imageData.data)
]).then(function(hashes){ ]);
var data = JSON.stringify({ const data = JSON.stringify({
urlHash: hashToString(hashes[0]), urlHash: hashToString(hashes[0]),
imageDataHash: hashToString(hashes[1]), imageDataHash: hashToString(hashes[1]),
isPointInPath isPointInPath
}, null, "\t"); }, null, "\t");
form.fingerprint.value = data; form.fingerprint.value = data;
var xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("POST", form.action, true); xhr.open("POST", form.action, true);
xhr.onreadystatechange = function(){ xhr.onreadystatechange = function(){
if (this.readyState === 4){ if (this.readyState === 4){
const status = this.status; const status = this.status;
if (status === 200 || status === 304) { if (status === 200 || status === 304) {
console.log("Sending xhr successful from", origin, ":", data); console.log("Sending xhr successful from", origin, ":", data);
}
else {
console.log("Sending xhr failed:", this);
}
} }
}; else {
xhr.send(new FormData(form)); console.log("Sending xhr failed:", this);
window.setTimeout(function(){ }
form.submit(); }
window.setTimeout( };
function(){ xhr.send(new FormData(form));
document.getElementById("log").textContent = window.setTimeout(function(){
"You see the real canvas fingerprint, but it cannot leak from this iFrame."; form.submit();
}, window.setTimeout(
250 function(){
); document.getElementById("log").textContent =
}, 1000); "You see the real canvas fingerprint, but it cannot leak from this iFrame.";
return; },
}); 250
);
}, 1000);
}; };
}(); }();

View File

@ -6,16 +6,16 @@
<link href="testIcon.svg" type="image/png" rel="icon"> <link href="testIcon.svg" type="image/png" rel="icon">
<link href="testIcon.svg" type="image/png" rel="shortcut icon"> <link href="testIcon.svg" type="image/png" rel="shortcut icon">
<script> <script>
var firstDescriptor = Object.getOwnPropertyDescriptor(HTMLCanvasElement.prototype, "getContext"); const firstDescriptor = Object.getOwnPropertyDescriptor(HTMLCanvasElement.prototype, "getContext");
console.log(new Date(), "starting first fingerprint", window.name); console.log(new Date(), "starting first fingerprint", window.name);
function fingerPrint(){ function fingerPrint(){
"use strict";var canvas = document.createElement("canvas"); "use strict";const canvas = document.createElement("canvas");
canvas.setAttribute("width", 220); canvas.setAttribute("width", 220);
canvas.setAttribute("height", 30); 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.textBaseline = "top";
ctx.font = "14px 'Arial'"; ctx.font = "14px 'Arial'";
ctx.textBaseline = "alphabetic"; ctx.textBaseline = "alphabetic";
@ -28,21 +28,20 @@
return canvas.toDataURL(); return canvas.toDataURL();
} }
function hash(url){ async function hash(url){
"use strict"; "use strict";
var buffer = new TextEncoder("utf-8").encode(url); const buffer = new TextEncoder("utf-8").encode(url);
return crypto.subtle.digest("SHA-256", buffer).then(function(hash){ const hash = await crypto.subtle.digest("SHA-256", buffer);
var chunks = []; const chunks = [];
(new Uint32Array(hash)).forEach(function(num){ (new Uint32Array(hash)).forEach(function(num){
chunks.push(num.toString(16)); chunks.push(num.toString(16));
});
return chunks.map(function(chunk){
return "0".repeat(8 - chunk.length) + chunk;
}).join("");
}); });
return chunks.map(function(chunk){
return "0".repeat(8 - chunk.length) + chunk;
}).join("");
} }
var firstFingerprint = false; let firstFingerprint = false;
try { try {
firstFingerprint = fingerPrint(); firstFingerprint = fingerPrint();
} }
@ -76,14 +75,14 @@
<div id="output"></div> <div id="output"></div>
<script> <script>
if (firstFingerprint){ if (firstFingerprint){
var output = document.getElementById("output"); const output = document.getElementById("output");
output.textContent = "context API not blocked"; output.textContent = "context API not blocked";
output.appendChild(document.createElement("br")); output.appendChild(document.createElement("br"));
window.setTimeout(function(){ window.setTimeout(async function(){
"use strict"; "use strict";
console.log(new Date(), "starting second fingerprint", window.name); 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){ if (firstDescriptor.value === secondDescriptor.value){
output.appendChild(document.createTextNode("descriptor did not change -> good!")); output.appendChild(document.createTextNode("descriptor did not change -> good!"));
@ -95,26 +94,22 @@
output.classList.add("nok"); output.classList.add("nok");
} }
output.appendChild(document.createElement("br")); output.appendChild(document.createElement("br"));
var secondFingerprint = fingerPrint(); const secondFingerprint = fingerPrint();
if (firstFingerprint === secondFingerprint){ if (firstFingerprint === secondFingerprint){
return hash(firstFingerprint).then(function(hash){ const firstHash = await hash(firstFingerprint);
output.appendChild(document.createTextNode("fingerprint consistent (" + hash + ") -> good!")); output.appendChild(document.createTextNode("fingerprint consistent (" + firstHash + ") -> good!"));
output.classList.add("ok"); output.classList.add("ok");
return;
});
} }
else { else {
return Promise.all([hash(firstFingerprint), hash(secondFingerprint)]).then(function(hashes){ const hashes = await Promise.all([hash(firstFingerprint), hash(secondFingerprint)]);
output.appendChild( output.appendChild(
document.createTextNode( document.createTextNode(
"fingerprint not consistent (" + "fingerprint not consistent (" +
hashes[0] + " != " + hashes[1] + hashes[0] + " != " + hashes[1] +
") -> very bad! (potential fingerprint leak)" ") -> very bad! (potential fingerprint leak)"
) )
); );
output.classList.add("nok"); output.classList.add("nok");
return;
});
} }
}, 500); }, 500);
} }

View File

@ -2,7 +2,7 @@
"use strict"; "use strict";
function hashToString(hash){ function hashToString(hash){
var chunks = []; const chunks = [];
(new Uint32Array(hash)).forEach(function(num){ (new Uint32Array(hash)).forEach(function(num){
chunks.push(num.toString(16)); chunks.push(num.toString(16));
}); });
@ -11,22 +11,18 @@
}).join(""); }).join("");
} }
function show(container, {url, imageData, isPointInPath}){ async function show(container, {url, imageData, isPointInPath}){
var display = container.querySelector(".display"); const display = container.querySelector(".display");
display.src = url; display.src = url;
display.title = url; display.title = url;
var buffer = new TextEncoder("utf-8").encode(url); const buffer = new TextEncoder("utf-8").encode(url);
Promise.all([ const hashes = await Promise.all([
crypto.subtle.digest("SHA-256", buffer), crypto.subtle.digest("SHA-256", buffer),
crypto.subtle.digest("SHA-256", imageData.data) crypto.subtle.digest("SHA-256", imageData.data)
]).then(function(hashes){ ]);
container.querySelector(".hash").textContent = container.querySelector(".hash").textContent =
hashToString(hashes[0]) + " / " + hashToString(hashes[0]) + " / " +
hashToString(hashes[1]); hashToString(hashes[1]);
return;
}).catch(function(error){
container.querySelector(".hash").textContent = "Error while calculating hash: " + error;
});
container.querySelector(".isPointInPath").textContent = isPointInPath; container.querySelector(".isPointInPath").textContent = isPointInPath;
} }
@ -47,7 +43,7 @@
catch (error){console.error(error);} catch (error){console.error(error);}
window.addEventListener("click", function windowOpenTest(){ window.addEventListener("click", function windowOpenTest(){
window.removeEventListener("click", windowOpenTest); window.removeEventListener("click", windowOpenTest);
var newWindow = window.open("/"); const newWindow = window.open("/");
try{ try{
show(document.getElementById("windowOpen"), copyToDifferentDocumentTest(newWindow.document)); show(document.getElementById("windowOpen"), copyToDifferentDocumentTest(newWindow.document));
} }
@ -79,7 +75,7 @@
show(document.getElementById("iframe6"), dynamicIframeTest3()); show(document.getElementById("iframe6"), dynamicIframeTest3());
}); });
document.querySelector("#windowOpen button").addEventListener("click", function(){ document.querySelector("#windowOpen button").addEventListener("click", function(){
var newWindow = window.open("/"); const newWindow = window.open("/");
show(document.getElementById("windowOpen"), copyToDifferentDocumentTest(newWindow.document)); show(document.getElementById("windowOpen"), copyToDifferentDocumentTest(newWindow.document));
newWindow.close(); newWindow.close();
}); });
@ -91,9 +87,9 @@ function draw(canvas){
canvas.setAttribute("width", 220); canvas.setAttribute("width", 220);
canvas.setAttribute("height", 30); 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.textBaseline = "top";
ctx.font = "14px 'Arial'"; ctx.font = "14px 'Arial'";
ctx.textBaseline = "alphabetic"; ctx.textBaseline = "alphabetic";
@ -123,9 +119,9 @@ function topTest(){
"use strict"; "use strict";
// create window canvas // create window canvas
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
// draw image in window canvas // draw image in window canvas
var ctx = draw(canvas); const ctx = draw(canvas);
return { return {
imageData: ctx.getImageData(0, 0, canvas.width, canvas.height), imageData: ctx.getImageData(0, 0, canvas.width, canvas.height),
url: canvas.toDataURL(), url: canvas.toDataURL(),
@ -137,16 +133,16 @@ function copyToDifferentDocumentTest(otherDocument){
"use strict"; "use strict";
// create window canvas // create window canvas
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
// draw image in window canvas // draw image in window canvas
draw(canvas); draw(canvas);
// create other canvas and context // create other canvas and context
var otherCanvas = otherDocument.createElement("canvas"); const otherCanvas = otherDocument.createElement("canvas");
otherCanvas.setAttribute("width", 220); otherCanvas.setAttribute("width", 220);
otherCanvas.setAttribute("height", 30); otherCanvas.setAttribute("height", 30);
var otherContext = otherCanvas.getContext("2d"); const otherContext = otherCanvas.getContext("2d");
// copy image from window canvas to iframe context // copy image from window canvas to iframe context
otherContext.drawImage(canvas, 0, 0); otherContext.drawImage(canvas, 0, 0);
@ -167,10 +163,10 @@ function iframeTest(iframe){
function dynamicIframeTest1(){ function dynamicIframeTest1(){
"use strict"; "use strict";
var length = frames.length; const length = frames.length;
var iframe = document.createElement("iframe"); const iframe = document.createElement("iframe");
document.body.appendChild(iframe); document.body.appendChild(iframe);
var iframeWindow = frames[length]; const iframeWindow = frames[length];
document.body.removeChild(iframe); document.body.removeChild(iframe);
return copyToDifferentDocumentTest(iframeWindow.document); return copyToDifferentDocumentTest(iframeWindow.document);
} }
@ -178,10 +174,10 @@ function dynamicIframeTest1(){
function dynamicIframeTest2(){ function dynamicIframeTest2(){
"use strict"; "use strict";
var length = window.length; const length = window.length;
var iframe = document.createElement("iframe"); const iframe = document.createElement("iframe");
document.body.appendChild(iframe); document.body.appendChild(iframe);
var iframeWindow = window[length]; const iframeWindow = window[length];
document.body.removeChild(iframe); document.body.removeChild(iframe);
return copyToDifferentDocumentTest(iframeWindow.document); return copyToDifferentDocumentTest(iframeWindow.document);
} }
@ -189,11 +185,11 @@ function dynamicIframeTest2(){
function dynamicIframeTest3(){ function dynamicIframeTest3(){
"use strict"; "use strict";
var length = window.length; const length = window.length;
var div = document.createElement("div"); const div = document.createElement("div");
document.body.appendChild(div); document.body.appendChild(div);
div.innerHTML = "<iframe></iframe>"; div.innerHTML = "<iframe></iframe>";
var iframeWindow = window[length]; const iframeWindow = window[length];
document.body.removeChild(div); document.body.removeChild(div);
return copyToDifferentDocumentTest(iframeWindow.document); return copyToDifferentDocumentTest(iframeWindow.document);
} }

View File

@ -3,9 +3,9 @@
function getParameters(context){ function getParameters(context){
const parameters = []; const parameters = [];
for (var name in context){ for (let name in context){
if (name.toUpperCase() === name){ if (name.toUpperCase() === name){
var value = context.getParameter(context[name]); const value = context.getParameter(context[name]);
if (value !== null){ if (value !== null){
parameters.push({name: name, value: value}); parameters.push({name: name, value: value});
} }
@ -13,18 +13,18 @@
} }
const debugExtension = context.getExtension("WEBGL_debug_renderer_info"); const debugExtension = context.getExtension("WEBGL_debug_renderer_info");
for (name in debugExtension){ for (let name in debugExtension){
if (name.toUpperCase() === name){ if (name.toUpperCase() === name){
value = context.getParameter(debugExtension[name]); const value = context.getParameter(debugExtension[name]);
if (value !== null){ if (value !== null){
parameters.push({name: name, value: value}); 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){ parameters.sort(function(a, b){
var frontA = frontParameters.indexOf(a.name); const frontA = frontParameters.indexOf(a.name);
var frontB = frontParameters.indexOf(b.name); const frontB = frontParameters.indexOf(b.name);
if (frontA !== -1){ if (frontA !== -1){
if (frontB !== -1){ if (frontB !== -1){
return frontA - frontB; return frontA - frontB;
@ -45,58 +45,55 @@
return parameters; return parameters;
} }
["webgl", "webgl2"].forEach(function(context, index){ ["webgl", "webgl2"].forEach(async function(context, index){
var output = document.createElement("div"); const output = document.createElement("div");
document.getElementById("output").appendChild(output); document.getElementById("output").appendChild(output);
try { try {
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
canvas.width = 11; canvas.width = 11;
canvas.height = 13; 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 // paint it completely black
gl.clearColor(index * 0.25, index * 0.25, index * 0.25, 1); gl.clearColor(index * 0.25, index * 0.25, index * 0.25, 1);
gl.clear(gl.COLOR_BUFFER_BIT); 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); gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
var values = {}; const values = {};
var max = 0; let max = 0;
for (var i = 0; i < pixels.length; i += 1){ for (let i = 0; i < pixels.length; i += 1){
values[pixels[i]] = (values[pixels[i]] || 0) + 1; values[pixels[i]] = (values[pixels[i]] || 0) + 1;
max = Math.max(max, values[pixels[i]]); max = Math.max(max, values[pixels[i]]);
} }
const parameters = getParameters(gl); const parameters = getParameters(gl);
if (context === "webgl2"){ if (context === "webgl2"){
var parameterOutput = document.createElement("table"); const parameterOutput = document.createElement("table");
document.getElementById("parameters").appendChild(parameterOutput); document.getElementById("parameters").appendChild(parameterOutput);
parameters.forEach(function(parameter){ parameters.forEach(function(parameter){
var parameterRow = document.createElement("tr"); const parameterRow = document.createElement("tr");
parameterRow.innerHTML = "<td>" + parameter.name + "</td><td>" + parameter.value + "</td>"; parameterRow.innerHTML = "<td>" + parameter.name + "</td><td>" + parameter.value + "</td>";
parameterOutput.appendChild(parameterRow); parameterOutput.appendChild(parameterRow);
}); });
} }
crypto.subtle.digest("SHA-256", new TextEncoder("utf-8").encode(parameters.map(function(parameter){ const hashBytes = await crypto.subtle.digest("SHA-256", new TextEncoder("utf-8")
return parameter.name + ": " + parameter.value; .encode(parameters.map(function(parameter){
}).join(","))) return parameter.name + ": " + parameter.value;
.then(function(hash){ }).join(",")));
var chunks = [];
(new Uint32Array(hash)).forEach(function(num){ const chunks = [];
chunks.push(num.toString(16)); (new Uint32Array(hashBytes)).forEach(function(num){
}); chunks.push(num.toString(16));
return chunks.map(function(chunk){ });
return "0".repeat(8 - chunk.length) + chunk; const hash = chunks.map(function(chunk){
}).join(""); return "0".repeat(8 - chunk.length) + chunk;
}).then(function(hash) { }).join("");
output.textContent = context + ": " +
(max !== 3 * values[255]? "": "not ") + "supported " + output.textContent = context + ": " +
"(parameter hash: " + hash + ")"; (max !== 3 * values[255]? "": "not ") + "supported " +
output.title = JSON.stringify(values); "(parameter hash: " + hash + ")";
return; output.title = JSON.stringify(values);
}).catch(function(error){
output.textContent = "Error while calculating hash: " + error;
});
} }
catch (error){ catch (error){
output.textContent = context + ": ERROR"; output.textContent = context + ": ERROR";