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-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"
}
},

View File

@ -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;

View File

@ -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);
}());

View File

@ -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;
}());

View File

@ -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));
};
}();

View File

@ -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);
});

View File

@ -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();
});

View File

@ -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);

View File

@ -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);
}
}());

View File

@ -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);
}

View File

@ -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 = "";
}

View File

@ -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);

View File

@ -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);
};
}();

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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";