(default settings)
|
brave 1.62 |
chrome 122.6261 |
duckduckgo 7.110 |
edge 122.2365 |
firefox 123.3 |
focus 123.0 |
opera 4.5 |
safari 17.4 |
vivaldi 6.3212 |
yandex 2401.7 |
|---|---|---|---|---|---|---|---|---|---|---|
|
State Partitioning testsWhich browsers isolate websites to prevent them from sharing data to track you?
A common vulnerability of web browsers is that they allow tracking companies to 'tag' your browser with some data ('state') that identifies you. When third-party trackers are embedded in websites, they can see this identifying data as you browse to different websites. Fortunately, it is possible for this category of leaks to be fixed by partitioning all data stored in the browser such that no data can be shared between websites.
| ||||||||||
|
Alt-Svc
Alt-Svc allows the server to indicate to the web browser that a resource should be loaded on a different server. Because this is a persistent setting, it could be used to track users across websites if it is not correctly partitioned.
| write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party: h3, h3, h3, h3, h3
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party: h3, h3, h3, h3, h3
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
h3,
Error: Unsupported,
Error: Unsupported
result, different first party: h3, h3, h2, h3, h3
unsupported: true, true, false, true, true
passed: , true
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party: h3, h3, h3, h3, h3
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
h3
result, different first party: h3, h3, h3, h3, h2
unsupported: true, true, true, true, false
passed: , true
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party: h3, h3, h3, h3, h2
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party: h3, h3, h2, h3, h3
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party: h3, h2, h3, h3, h2
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
h3,
h3
result, different first party: h3, h3, h3, h2, h2
unsupported: true, true, true, false, false
passed: , true, true
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same first party:
Error: Unsupported,
Error: Unsupported
result, different first party: h3, h3
unsupported: true, true
passed: undefined
test failed: false, false
|
|
blob
A 'blob URL' is a local reference to some raw data. Trackers can use a blob URL to share data between websites.
| write: (secret) => {
try {
let blobURL = URL.createObjectURL(new Blob([secret]));
fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async (secret) => {
let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
let result = await response.json();
let blobUrl = decodeURIComponent(result.blobUrl);
let blobResponse = await fetch(blobUrl);
return blobResponse.text();
}
result, same first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
result, different first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let blobURL = URL.createObjectURL(new Blob([secret]));
fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async (secret) => {
let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
let result = await response.json();
let blobUrl = decodeURIComponent(result.blobUrl);
let blobResponse = await fetch(blobUrl);
return blobResponse.text();
}
result, same first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
result, different first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let blobURL = URL.createObjectURL(new Blob([secret]));
fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async (secret) => {
let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
let result = await response.json();
let blobUrl = decodeURIComponent(result.blobUrl);
let blobResponse = await fetch(blobUrl);
return blobResponse.text();
}
result, same first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
result, different first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let blobURL = URL.createObjectURL(new Blob([secret]));
fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async (secret) => {
let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
let result = await response.json();
let blobUrl = decodeURIComponent(result.blobUrl);
let blobResponse = await fetch(blobUrl);
return blobResponse.text();
}
result, same first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
result, different first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let blobURL = URL.createObjectURL(new Blob([secret]));
fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async (secret) => {
let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
let result = await response.json();
let blobUrl = decodeURIComponent(result.blobUrl);
let blobResponse = await fetch(blobUrl);
return blobResponse.text();
}
result, same first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
result, different first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let blobURL = URL.createObjectURL(new Blob([secret]));
fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async (secret) => {
let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
let result = await response.json();
let blobUrl = decodeURIComponent(result.blobUrl);
let blobResponse = await fetch(blobUrl);
return blobResponse.text();
}
result, same first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
result, different first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let blobURL = URL.createObjectURL(new Blob([secret]));
fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async (secret) => {
let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
let result = await response.json();
let blobUrl = decodeURIComponent(result.blobUrl);
let blobResponse = await fetch(blobUrl);
return blobResponse.text();
}
result, same first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
result, different first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let blobURL = URL.createObjectURL(new Blob([secret]));
fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async (secret) => {
let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
let result = await response.json();
let blobUrl = decodeURIComponent(result.blobUrl);
let blobResponse = await fetch(blobUrl);
return blobResponse.text();
}
result, same first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
result, different first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let blobURL = URL.createObjectURL(new Blob([secret]));
fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async (secret) => {
let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
let result = await response.json();
let blobUrl = decodeURIComponent(result.blobUrl);
let blobResponse = await fetch(blobUrl);
return blobResponse.text();
}
result, same first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
result, different first party:
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed,
Error: Load failed
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let blobURL = URL.createObjectURL(new Blob([secret]));
fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async (secret) => {
let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
let result = await response.json();
let blobUrl = decodeURIComponent(result.blobUrl);
let blobResponse = await fetch(blobUrl);
return blobResponse.text();
}
result, same first party:
Error: Load failed,
Error: Load failed
result, different first party:
Error: Load failed,
Error: Load failed
unsupported: false, false
passed: undefined
test failed: true, true
|
|
BroadcastChannel
A BroadcastChannel is designed to send messages between tabs. In some browsers it can be used for cross-site communication and tracking.
| write: (secret) => {
try {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data === "request") {
bc.postMessage(secret);
}
};
} catch (e) {
throw new Error("Unsupported");
}
}
read: () =>
new Promise((resolve, reject) => {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data !== "request") {
resolve(event.data);
}
};
bc.postMessage("request");
setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
})
result, same first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
result, different first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data === "request") {
bc.postMessage(secret);
}
};
} catch (e) {
throw new Error("Unsupported");
}
}
read: () =>
new Promise((resolve, reject) => {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data !== "request") {
resolve(event.data);
}
};
bc.postMessage("request");
setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
})
result, same first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
result, different first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data === "request") {
bc.postMessage(secret);
}
};
} catch (e) {
throw new Error("Unsupported");
}
}
read: () =>
new Promise((resolve, reject) => {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data !== "request") {
resolve(event.data);
}
};
bc.postMessage("request");
setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
})
result, same first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
result, different first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data === "request") {
bc.postMessage(secret);
}
};
} catch (e) {
throw new Error("Unsupported");
}
}
read: () =>
new Promise((resolve, reject) => {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data !== "request") {
resolve(event.data);
}
};
bc.postMessage("request");
setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
})
result, same first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
result, different first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data === "request") {
bc.postMessage(secret);
}
};
} catch (e) {
throw new Error("Unsupported");
}
}
read: () =>
new Promise((resolve, reject) => {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data !== "request") {
resolve(event.data);
}
};
bc.postMessage("request");
setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
})
result, same first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
result, different first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data === "request") {
bc.postMessage(secret);
}
};
} catch (e) {
throw new Error("Unsupported");
}
}
read: () =>
new Promise((resolve, reject) => {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data !== "request") {
resolve(event.data);
}
};
bc.postMessage("request");
setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
})
result, same first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
result, different first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data === "request") {
bc.postMessage(secret);
}
};
} catch (e) {
throw new Error("Unsupported");
}
}
read: () =>
new Promise((resolve, reject) => {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data !== "request") {
resolve(event.data);
}
};
bc.postMessage("request");
setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
})
result, same first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
result, different first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data === "request") {
bc.postMessage(secret);
}
};
} catch (e) {
throw new Error("Unsupported");
}
}
read: () =>
new Promise((resolve, reject) => {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data !== "request") {
resolve(event.data);
}
};
bc.postMessage("request");
setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
})
result, same first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
result, different first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data === "request") {
bc.postMessage(secret);
}
};
} catch (e) {
throw new Error("Unsupported");
}
}
read: () =>
new Promise((resolve, reject) => {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data !== "request") {
resolve(event.data);
}
};
bc.postMessage("request");
setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
})
result, same first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
result, different first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: (secret) => {
try {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data === "request") {
bc.postMessage(secret);
}
};
} catch (e) {
throw new Error("Unsupported");
}
}
read: () =>
new Promise((resolve, reject) => {
let bc = new BroadcastChannel("secrets");
bc.onmessage = (event) => {
if (event.data !== "request") {
resolve(event.data);
}
};
bc.postMessage("request");
setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
})
result, same first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
result, different first party:
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false
passed: undefined
test failed: true, true
|
|
CacheStorage
The Cache API is a content storage mechanism originally introduced to support ServiceWorkers. If the same Cache object is accessible to multiple websites, it can be abused to track users.
| write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same first party:
2867a272-3459-465c-88f5-c8c0601a7e8c,
a017c498-69a8-4e1c-bde8-188025a5fc0e,
843217a8-9a9f-4727-84fe-c9023de914c6,
c223fe48-a77f-4087-a844-72878ae074ce,
2f51ceb9-93b5-4f31-8fc6-26429268e18b
result, different first party:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same first party:
3381129f-ab1e-4185-897a-9961f05c5a89,
8a73aa6a-249f-456f-aa36-7d299a7f4bab,
aea7d626-1f84-4d5f-aa5f-f481621f7a77,
ec31548d-380e-405b-8425-e1f9a359fe1b,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234
result, different first party:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same first party:
c846cb44-12d2-41b3-b2a6-38ca1a4518ed,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed
result, different first party:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same first party:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f,
d648fea1-92c9-4677-98bc-37477e3a6935,
7c2ccb5a-8757-43b0-809c-92693c5728a8,
d6429a17-238b-4b71-8e41-ab16a05fd3e2,
8a22edfa-bdde-4576-aae0-5a5b2459fe17
result, different first party:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same first party:
81be7770-de26-46ba-a344-6855e2651f4e,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51,
b1fae351-a866-4214-a922-24e8eba8325e,
f27fcb7b-0656-432b-a99c-29f0069fc109,
52835348-3201-47b2-8505-8c648dbd0432
result, different first party:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same first party:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
result, different first party:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same first party:
89fb71a3-e670-4f00-a55d-b90e9aed4261,
2938ddfd-f5a0-4245-b96d-e8c629a66498,
a8cbb18e-e019-4292-80b6-78528b4dae06,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe
result, different first party:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same first party:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
result, different first party:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same first party:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55,
f3bd2113-34b8-4cc3-802e-76d69472d962,
c240f51b-e239-4982-8f53-824fc630fb9f,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e,
64712c28-73a1-4e49-b42b-2c37152d98cb
result, different first party:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same first party:
8a2376aa-acdf-4f53-99e8-517a83b724eb,
8a2376aa-acdf-4f53-99e8-517a83b724eb
result, different first party:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false
passed: true, true
test failed: false, false
|
|
cookie (HTTP)
The cookie, first introduced by Netscape in 1994, is a small amount of data stored by your browser on a website's behalf. It has legitimate uses, but it is also the classic cross-site tracking mechanism, and today still the most popular method of tracking users across websites. Browsers can stop cookies from being used for cross-site tracking by either blocking or partitioning them.
| write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same first party:
2867a272-3459-465c-88f5-c8c0601a7e8c_http,
a017c498-69a8-4e1c-bde8-188025a5fc0e_http,
843217a8-9a9f-4727-84fe-c9023de914c6_http,
c223fe48-a77f-4087-a844-72878ae074ce_http,
2f51ceb9-93b5-4f31-8fc6-26429268e18b_http
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same first party:
3381129f-ab1e-4185-897a-9961f05c5a89_http,
8a73aa6a-249f-456f-aa36-7d299a7f4bab_http,
aea7d626-1f84-4d5f-aa5f-f481621f7a77_http,
ec31548d-380e-405b-8425-e1f9a359fe1b_http,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234_http
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same first party:
e3162a38-9dff-4f7a-8c43-a42c5e945177_http,
d940278d-5bd6-490e-9de4-a5ce7c6e3346_http,
93972f1c-4b55-48e7-b36c-89c55f81499d_http,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3_http,
052bd326-3e1e-457e-a2a1-4de5bceb1764_http
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same first party:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f_http,
d648fea1-92c9-4677-98bc-37477e3a6935_http,
7c2ccb5a-8757-43b0-809c-92693c5728a8_http,
d6429a17-238b-4b71-8e41-ab16a05fd3e2_http,
8a22edfa-bdde-4576-aae0-5a5b2459fe17_http
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same first party:
81be7770-de26-46ba-a344-6855e2651f4e_http,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51_http,
b1fae351-a866-4214-a922-24e8eba8325e_http,
f27fcb7b-0656-432b-a99c-29f0069fc109_http,
52835348-3201-47b2-8505-8c648dbd0432_http
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same first party:
df336d08-f790-4b43-8c0d-f035748ce923_http,
71e9a742-0190-4ced-b3ae-474d3c125c49_http,
11bbfc32-cceb-4e06-96f1-f616876b2aa7_http,
f0b86440-e911-449e-b536-1cee79072fc3_http,
afb7ec45-db89-49ab-aacf-f3765763d32d_http
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same first party:
89fb71a3-e670-4f00-a55d-b90e9aed4261_http,
2938ddfd-f5a0-4245-b96d-e8c629a66498_http,
a8cbb18e-e019-4292-80b6-78528b4dae06_http,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24_http,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe_http
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same first party:
45f08187-1804-4edc-8ea5-864c7aca7e23_http,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc_http,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc_http,
592b7313-24d0-455a-b9c4-2bbafee9d271_http,
5d215dd1-0fc4-4f5e-a510-b649d179ba24_http
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same first party:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55_http,
f3bd2113-34b8-4cc3-802e-76d69472d962_http,
c240f51b-e239-4982-8f53-824fc630fb9f_http,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e_http,
64712c28-73a1-4e49-b42b-2c37152d98cb_http
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same first party:
8a2376aa-acdf-4f53-99e8-517a83b724eb_http,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5_http
result, different first party: ,
unsupported: false, false
passed: true, true
test failed: false, false
|
|
cookie (JS)
The cookie, first introduced by Netscape in 1994, is a small amount of data stored by your browser on a website's behalf. It has legitimate uses, but it is also the classic cross-site tracking mechanism, and today still the most popular method of tracking users across websites. Browsers can stop cookies from being used for cross-site tracking by either blocking or partitioning them.
| write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party:
2867a272-3459-465c-88f5-c8c0601a7e8c_js,
a017c498-69a8-4e1c-bde8-188025a5fc0e_js,
843217a8-9a9f-4727-84fe-c9023de914c6_js,
c223fe48-a77f-4087-a844-72878ae074ce_js,
2f51ceb9-93b5-4f31-8fc6-26429268e18b_js
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party:
3381129f-ab1e-4185-897a-9961f05c5a89_js,
8a73aa6a-249f-456f-aa36-7d299a7f4bab_js,
aea7d626-1f84-4d5f-aa5f-f481621f7a77_js,
ec31548d-380e-405b-8425-e1f9a359fe1b_js,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234_js
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party:
e3162a38-9dff-4f7a-8c43-a42c5e945177_js,
d940278d-5bd6-490e-9de4-a5ce7c6e3346_js,
93972f1c-4b55-48e7-b36c-89c55f81499d_js,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3_js,
052bd326-3e1e-457e-a2a1-4de5bceb1764_js
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f_js,
d648fea1-92c9-4677-98bc-37477e3a6935_js,
7c2ccb5a-8757-43b0-809c-92693c5728a8_js,
d6429a17-238b-4b71-8e41-ab16a05fd3e2_js,
8a22edfa-bdde-4576-aae0-5a5b2459fe17_js
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party:
81be7770-de26-46ba-a344-6855e2651f4e_js,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51_js,
b1fae351-a866-4214-a922-24e8eba8325e_js,
f27fcb7b-0656-432b-a99c-29f0069fc109_js,
52835348-3201-47b2-8505-8c648dbd0432_js
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party:
df336d08-f790-4b43-8c0d-f035748ce923_js,
71e9a742-0190-4ced-b3ae-474d3c125c49_js,
11bbfc32-cceb-4e06-96f1-f616876b2aa7_js,
f0b86440-e911-449e-b536-1cee79072fc3_js,
afb7ec45-db89-49ab-aacf-f3765763d32d_js
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party:
89fb71a3-e670-4f00-a55d-b90e9aed4261_js,
2938ddfd-f5a0-4245-b96d-e8c629a66498_js,
a8cbb18e-e019-4292-80b6-78528b4dae06_js,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24_js,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe_js
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party:
45f08187-1804-4edc-8ea5-864c7aca7e23_js,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc_js,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc_js,
592b7313-24d0-455a-b9c4-2bbafee9d271_js,
5d215dd1-0fc4-4f5e-a510-b649d179ba24_js
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55_js,
f3bd2113-34b8-4cc3-802e-76d69472d962_js,
c240f51b-e239-4982-8f53-824fc630fb9f_js,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e_js,
64712c28-73a1-4e49-b42b-2c37152d98cb_js
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party:
8a2376aa-acdf-4f53-99e8-517a83b724eb_js,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5_js
result, different first party: ,
unsupported: false, false
passed: true, true
test failed: false, false
|
|
CookieStore
The Cookie Store API is an alternative asynchronous API for managing cookies, supported by some browsers.
| write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same first party:
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported
unsupported: true, true
passed: undefined
test failed: false, false
|
|
CSS cache
CSS stylesheets are cached, and if that cache is shared between websites, it can be used to track users across sites.
| write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same first party:
fake_14053319615570858,
fake_8804607770405792,
fake_9787261020842783,
fake_7927412604811908,
fake_9695619178747596
result, different first party:
fake_9814932416730708,
fake_9340427798899451,
fake_7195355728034678,
fake_10716557956978856,
fake_6461186299481834
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same first party:
fake_000964765922012889,
fake_6587881759282812,
fake_5904995953757772,
fake_35663500576335716,
fake_8287278091841175
result, different first party:
fake_4421888686356501,
fake_6285221566094721,
fake_9909368233318563,
fake_20053421864676335,
fake_7680514468469744
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same first party:
fake_5876556923345615,
fake_40459253038343435,
fake_6372337940519233,
fake_8391858059198303,
fake_8374057272443021
result, different first party:
fake_23264621538081376,
fake_6149547960981587,
fake_7306246599479616,
fake_9370103983493288,
fake_4516380306837071
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same first party:
fake_9234602612175702,
fake_3938079413184312,
fake_38334674642830335,
fake_7794903885885716,
fake_9148705233523324
result, different first party:
fake_4429144325036931,
fake_23749996365005654,
fake_7469127668190552,
fake_4970217815740736,
fake_13420285913129182
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same first party:
fake_8356735912588187,
fake_26182735904811505,
fake_5681823020495602,
fake_3256155734837656,
fake_9212157170418269
result, different first party:
fake_5535580587703732,
fake_9951282573584594,
fake_3235440526576625,
fake_8189643661734995,
fake_5638818367301739
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same first party:
fake_9571174637631477,
fake_1952827424158703,
fake_7207362283015957,
fake_23659248666778154,
fake_2531781932606265
result, different first party:
fake_709010638088851,
fake_8830582445829085,
fake_9839504770502521,
fake_7047131101280784,
fake_9542363343403055
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same first party:
fake_761609682976861,
fake_8234126652776206,
fake_3324957782273308,
fake_5080703134002686,
fake_9664304709585738
result, different first party:
fake_8445494339307693,
fake_4568362589845556,
fake_9249188292017148,
fake_7207916942143746,
fake_5995197652926711
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same first party:
fake_8237091451996934,
fake_7001695690626426,
fake_029913668717961084,
fake_29036920608772276,
fake_8892902292561995
result, different first party:
fake_44359229331999295,
fake_3689860863674417,
fake_16190426149743087,
fake_9768104578084145,
fake_7198597055059075
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same first party:
fake_5387216412895066,
fake_14876981554937818,
fake_8962271159799415,
fake_1546083399727638,
fake_06921134862048417
result, different first party:
fake_5077171558142841,
fake_21288064886119606,
fake_6588080355181118,
fake_2241432772246068,
fake_9135513334559513
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same first party:
fake_8110208339499625,
fake_6553396066805017
result, different first party:
fake_1465876885177111,
fake_43212454030714453
unsupported: false, false
passed: true, true
test failed: false, false
|
|
favicon cache
A favicon is an icon that represents a website, typically shown in browser tab and bookmarks menu. If the favicon cache is not partitioned, it can be used to track users across websites.
| write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same first party: 1, Error: No requests received, Error: No requests received, Error: No requests received, Error: No requests received
result, different first party: 1, Error: No requests received, Error: No requests received, Error: No requests received, Error: No requests received
unsupported: false, true, true, true, true
passed: false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same first party: 1, Error: No requests received, Error: No requests received, Error: No requests received, Error: No requests received
result, different first party: 1, Error: No requests received, Error: No requests received, Error: No requests received, Error: No requests received
unsupported: false, true, true, true, true
passed: false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same first party: 1, 2, 2, 2, 1
result, different first party: 1, 2, 2, 2, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same first party: 1, 1
result, different first party: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false
|
|
fetch cache
When a resource is received via the Fetch API, it is frequently cached. That cache can potentially be abused for cross-site tracking.
| write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 2, 2
result, different first party: 3, 3
unsupported: false, false
passed: true, true
test failed: false, false
|
|
font cache
Web fonts are sometimes stored in their own cache, which is vulnerable to being abused for cross-site tracking.
| write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2
result, different first party: 3, 3
unsupported: false, false
passed: true, true
test failed: false, false
|
|
getDirectory
navigator.storage.getDirectory exposes a location for storing files to web content. In some cases, these files may be shared across tabs.
| write: async (secret) => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt", { create: true });
const stream = await fileHandle.createWritable();
await stream.write(secret);
await stream.close();
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt");
const file = await fileHandle.getFile();
return file.text();
} catch (e) {
throw new Error("Unsupported");
}
}
result, same first party: , , , ,
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt", { create: true });
const stream = await fileHandle.createWritable();
await stream.write(secret);
await stream.close();
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt");
const file = await fileHandle.getFile();
return file.text();
} catch (e) {
throw new Error("Unsupported");
}
}
result, same first party: , , , ,
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt", { create: true });
const stream = await fileHandle.createWritable();
await stream.write(secret);
await stream.close();
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt");
const file = await fileHandle.getFile();
return file.text();
} catch (e) {
throw new Error("Unsupported");
}
}
result, same first party: , , , ,
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt", { create: true });
const stream = await fileHandle.createWritable();
await stream.write(secret);
await stream.close();
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt");
const file = await fileHandle.getFile();
return file.text();
} catch (e) {
throw new Error("Unsupported");
}
}
result, same first party: , , , ,
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt", { create: true });
const stream = await fileHandle.createWritable();
await stream.write(secret);
await stream.close();
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt");
const file = await fileHandle.getFile();
return file.text();
} catch (e) {
throw new Error("Unsupported");
}
}
result, same first party: , , , ,
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt", { create: true });
const stream = await fileHandle.createWritable();
await stream.write(secret);
await stream.close();
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt");
const file = await fileHandle.getFile();
return file.text();
} catch (e) {
throw new Error("Unsupported");
}
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt", { create: true });
const stream = await fileHandle.createWritable();
await stream.write(secret);
await stream.close();
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt");
const file = await fileHandle.getFile();
return file.text();
} catch (e) {
throw new Error("Unsupported");
}
}
result, same first party: , , , ,
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt", { create: true });
const stream = await fileHandle.createWritable();
await stream.write(secret);
await stream.close();
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt");
const file = await fileHandle.getFile();
return file.text();
} catch (e) {
throw new Error("Unsupported");
}
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt", { create: true });
const stream = await fileHandle.createWritable();
await stream.write(secret);
await stream.close();
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt");
const file = await fileHandle.getFile();
return file.text();
} catch (e) {
throw new Error("Unsupported");
}
}
result, same first party: , , , ,
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt", { create: true });
const stream = await fileHandle.createWritable();
await stream.write(secret);
await stream.close();
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
try {
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle("secret.txt");
const file = await fileHandle.getFile();
return file.text();
} catch (e) {
throw new Error("Unsupported");
}
}
result, same first party: ,
result, different first party:
Error: Unsupported,
Error: Unsupported
unsupported: true, true
passed: undefined
test failed: false, false
|
|
H1 connection
HTTP/1.x are the classic web connection protocols. If these connections are re-used across websites, they can be used to track users.
| write: async (secret) => {
await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
2867a272-3459-465c-88f5-c8c0601a7e8c,
a017c498-69a8-4e1c-bde8-188025a5fc0e,
843217a8-9a9f-4727-84fe-c9023de914c6,
c223fe48-a77f-4087-a844-72878ae074ce,
2f51ceb9-93b5-4f31-8fc6-26429268e18b
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
3381129f-ab1e-4185-897a-9961f05c5a89,
8a73aa6a-249f-456f-aa36-7d299a7f4bab,
aea7d626-1f84-4d5f-aa5f-f481621f7a77,
ec31548d-380e-405b-8425-e1f9a359fe1b,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
e3162a38-9dff-4f7a-8c43-a42c5e945177,
d940278d-5bd6-490e-9de4-a5ce7c6e3346,
93972f1c-4b55-48e7-b36c-89c55f81499d,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3,
052bd326-3e1e-457e-a2a1-4de5bceb1764
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f,
d648fea1-92c9-4677-98bc-37477e3a6935,
7c2ccb5a-8757-43b0-809c-92693c5728a8,
d6429a17-238b-4b71-8e41-ab16a05fd3e2,
8a22edfa-bdde-4576-aae0-5a5b2459fe17
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
81be7770-de26-46ba-a344-6855e2651f4e,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51,
,
f27fcb7b-0656-432b-a99c-29f0069fc109,
52835348-3201-47b2-8505-8c648dbd0432
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true
test failed: false, false, true, false, false
|
write: async (secret) => {
await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
df336d08-f790-4b43-8c0d-f035748ce923,
71e9a742-0190-4ced-b3ae-474d3c125c49,
11bbfc32-cceb-4e06-96f1-f616876b2aa7,
f0b86440-e911-449e-b536-1cee79072fc3,
afb7ec45-db89-49ab-aacf-f3765763d32d
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
89fb71a3-e670-4f00-a55d-b90e9aed4261,
2938ddfd-f5a0-4245-b96d-e8c629a66498,
a8cbb18e-e019-4292-80b6-78528b4dae06,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
45f08187-1804-4edc-8ea5-864c7aca7e23,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc,
592b7313-24d0-455a-b9c4-2bbafee9d271,
5d215dd1-0fc4-4f5e-a510-b649d179ba24
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55,
f3bd2113-34b8-4cc3-802e-76d69472d962,
c240f51b-e239-4982-8f53-824fc630fb9f,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e,
64712c28-73a1-4e49-b42b-2c37152d98cb
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
8a2376aa-acdf-4f53-99e8-517a83b724eb,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5
result, different first party: ,
unsupported: false, false
passed: true, true
test failed: false, false
|
|
H2 connection
HTTP/2 is a web connection protocol introduced in 2015. Some browsers re-use HTTP/2 connections across websites and can thus be used to track users.
| write: async (secret) => {
await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
2867a272-3459-465c-88f5-c8c0601a7e8c,
a017c498-69a8-4e1c-bde8-188025a5fc0e,
843217a8-9a9f-4727-84fe-c9023de914c6,
c223fe48-a77f-4087-a844-72878ae074ce,
2f51ceb9-93b5-4f31-8fc6-26429268e18b
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
3381129f-ab1e-4185-897a-9961f05c5a89,
8a73aa6a-249f-456f-aa36-7d299a7f4bab,
aea7d626-1f84-4d5f-aa5f-f481621f7a77,
ec31548d-380e-405b-8425-e1f9a359fe1b,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
e3162a38-9dff-4f7a-8c43-a42c5e945177,
d940278d-5bd6-490e-9de4-a5ce7c6e3346,
93972f1c-4b55-48e7-b36c-89c55f81499d,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3,
052bd326-3e1e-457e-a2a1-4de5bceb1764
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f,
d648fea1-92c9-4677-98bc-37477e3a6935,
7c2ccb5a-8757-43b0-809c-92693c5728a8,
d6429a17-238b-4b71-8e41-ab16a05fd3e2,
8a22edfa-bdde-4576-aae0-5a5b2459fe17
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
81be7770-de26-46ba-a344-6855e2651f4e,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51,
b1fae351-a866-4214-a922-24e8eba8325e,
f27fcb7b-0656-432b-a99c-29f0069fc109,
52835348-3201-47b2-8505-8c648dbd0432
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
df336d08-f790-4b43-8c0d-f035748ce923,
71e9a742-0190-4ced-b3ae-474d3c125c49,
11bbfc32-cceb-4e06-96f1-f616876b2aa7,
f0b86440-e911-449e-b536-1cee79072fc3,
afb7ec45-db89-49ab-aacf-f3765763d32d
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
89fb71a3-e670-4f00-a55d-b90e9aed4261,
2938ddfd-f5a0-4245-b96d-e8c629a66498,
a8cbb18e-e019-4292-80b6-78528b4dae06,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
45f08187-1804-4edc-8ea5-864c7aca7e23,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc,
592b7313-24d0-455a-b9c4-2bbafee9d271,
5d215dd1-0fc4-4f5e-a510-b649d179ba24
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55,
f3bd2113-34b8-4cc3-802e-76d69472d962,
c240f51b-e239-4982-8f53-824fc630fb9f,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e,
64712c28-73a1-4e49-b42b-2c37152d98cb
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
}
read: async () => {
let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
return await response.text();
}
result, same first party:
8a2376aa-acdf-4f53-99e8-517a83b724eb,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5
result, different first party: ,
unsupported: false, false
passed: true, true
test failed: false, false
|
|
H3 connection
HTTP/3 is a new standard HTTP connection protocol, still in draft but widely supported by browsers. If it is not partitioned, it can be used to track users across websites.
| write: async (secret) => {
// Ensure that we can switch over to h3 via alt-svc:
for (let i = 0; i<3; ++i) {
await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
await sleepMs(500);
}
// Are we now connecting over h3?
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
let text = await response.text();
// Empty response text indicates we are not connecting over h3:
if (text.trim() === "") {
throw new Error("Unsupported");
}
}
read: async () => {
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
return await response.text();
}
result, same first party: , fb038cdf4c6d6eebc97db67db5a45e98, a361071bec15217a4c3125dcbc1b8616, 1a728d54117c793a343bbc45f4910be4, 1e0a2b423afb0a6d0e07da478fa58b38
result, different first party: , 3945f6d94d5d1ba7aeb03e3ca26f2ae9, cea38661dca39dd1506512d6bc859f4a, 18192d96c381c62d33084958f44e0abf, c6eabd556cb9b8c4413264d0cf7978e2
unsupported: true, false, false, false, false
passed: , true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Ensure that we can switch over to h3 via alt-svc:
for (let i = 0; i<3; ++i) {
await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
await sleepMs(500);
}
// Are we now connecting over h3?
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
let text = await response.text();
// Empty response text indicates we are not connecting over h3:
if (text.trim() === "") {
throw new Error("Unsupported");
}
}
read: async () => {
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
return await response.text();
}
result, same first party: , 45c310d5333b3a19ae5b8c780a9cd7d2, bb92dfb9e0d382bab06c93207675858f, 396cd1f0b8093ca724b9754e69d40b0e, b9634e6aeffaa01573c752ce57b88157
result, different first party: , 0953f973b50cbaf28f61900440c8292b, 312883ac1ce9aee61662fac8f27eddc3, 4461dd92eea1db4e4c88382d2c6fb593, b0e934a818cc3573c846b8963aa62bda
unsupported: true, false, false, false, false
passed: , true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Ensure that we can switch over to h3 via alt-svc:
for (let i = 0; i<3; ++i) {
await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
await sleepMs(500);
}
// Are we now connecting over h3?
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
let text = await response.text();
// Empty response text indicates we are not connecting over h3:
if (text.trim() === "") {
throw new Error("Unsupported");
}
}
read: async () => {
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
return await response.text();
}
result, same first party: , , 331fcec934ec8166f7b631d12a32f169, 4032df90c0c4929684ae97c545f1b2f3, 50218e8498942d4fdab466fd66aab899
result, different first party: , 61f6d7aa6ff87e53336e15d56a62eff7, , 0f5c7ad49e96e2ac16df31f6b4c5dd4a, 9eddb0be1b41d0ecc7ffe3663c429960
unsupported: true, true, false, false, false
passed: , true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Ensure that we can switch over to h3 via alt-svc:
for (let i = 0; i<3; ++i) {
await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
await sleepMs(500);
}
// Are we now connecting over h3?
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
let text = await response.text();
// Empty response text indicates we are not connecting over h3:
if (text.trim() === "") {
throw new Error("Unsupported");
}
}
read: async () => {
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
return await response.text();
}
result, same first party: , cc8c2ca8f39c3bbed8f03cdc1a90e341, be1526484d9273186a19567631783a96, 45eb32e10efc517811369b30384b3611, 93adfac24b1f888ef0251d851404cc49
result, different first party: , 3b115346e0620edf113cc70f239f2854, fa05cd995193b3199dba3ec050fc22ec, 2921dbb45623511c8e16f2328d60360c, fade71c8b3fe783526a1bae9529e7665
unsupported: true, false, false, false, false
passed: , true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Ensure that we can switch over to h3 via alt-svc:
for (let i = 0; i<3; ++i) {
await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
await sleepMs(500);
}
// Are we now connecting over h3?
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
let text = await response.text();
// Empty response text indicates we are not connecting over h3:
if (text.trim() === "") {
throw new Error("Unsupported");
}
}
read: async () => {
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
return await response.text();
}
result, same first party: , 5bb35463cbcc9c81a88817bd22a5565a, c217bf7890ca5771fbdc347399fc4fa0, a76fa92c1a9c8261ba062da8662470d7, 566860bdb53a0be2d6ed8264f7f0fa3f
result, different first party: , 0e4d3b39e09a699974bc66f84736afb3, a66550077e58a7adfec51eb746838f91, fba2ccafd0c5f54a0cae7a4a22a79a9d,
unsupported: true, false, false, false, false
passed: , true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Ensure that we can switch over to h3 via alt-svc:
for (let i = 0; i<3; ++i) {
await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
await sleepMs(500);
}
// Are we now connecting over h3?
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
let text = await response.text();
// Empty response text indicates we are not connecting over h3:
if (text.trim() === "") {
throw new Error("Unsupported");
}
}
read: async () => {
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
return await response.text();
}
result, same first party: , , , ,
result, different first party: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
// Ensure that we can switch over to h3 via alt-svc:
for (let i = 0; i<3; ++i) {
await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
await sleepMs(500);
}
// Are we now connecting over h3?
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
let text = await response.text();
// Empty response text indicates we are not connecting over h3:
if (text.trim() === "") {
throw new Error("Unsupported");
}
}
read: async () => {
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
return await response.text();
}
result, same first party: , a7a6503321560ff66bb1bce485c04c08, a4cb67d9e993b233afe28dc9836c06c6, 9886fee4f087c4fa180513b327f1bbd7, 2dd84cb7ab561ad51b1f0debb4e7e231
result, different first party: , e2ba772da504b41b3babcac875c94b76, 0bb767c3829ca34299d5d009bfc8bf48, 8bce9bd6914d44a1845d4aacd86a84f1, bde5dda612d72a8adfdaa3e8de072eba
unsupported: true, false, false, false, false
passed: , true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Ensure that we can switch over to h3 via alt-svc:
for (let i = 0; i<3; ++i) {
await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
await sleepMs(500);
}
// Are we now connecting over h3?
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
let text = await response.text();
// Empty response text indicates we are not connecting over h3:
if (text.trim() === "") {
throw new Error("Unsupported");
}
}
read: async () => {
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
return await response.text();
}
result, same first party: , , , ,
result, different first party: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
// Ensure that we can switch over to h3 via alt-svc:
for (let i = 0; i<3; ++i) {
await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
await sleepMs(500);
}
// Are we now connecting over h3?
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
let text = await response.text();
// Empty response text indicates we are not connecting over h3:
if (text.trim() === "") {
throw new Error("Unsupported");
}
}
read: async () => {
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
return await response.text();
}
result, same first party: , 911a4eb9f370cfb135d4ceaa30921b83, dbe4c3bd8b988b7088787416770605e9, b0caab0242656820c6e41bb0e94d84c0, b41ca40770ef1218e27b84c322bc0137
result, different first party: , d71c5e9a72a6ddfcee2988e21264ccc3, 2452ef6172595b384bded085312d43b2, ,
unsupported: true, false, false, false, false
passed: , true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Ensure that we can switch over to h3 via alt-svc:
for (let i = 0; i<3; ++i) {
await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
await sleepMs(500);
}
// Are we now connecting over h3?
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
let text = await response.text();
// Empty response text indicates we are not connecting over h3:
if (text.trim() === "") {
throw new Error("Unsupported");
}
}
read: async () => {
let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
return await response.text();
}
result, same first party: ,
result, different first party: ,
unsupported: true, true
passed: undefined
test failed: false, false
|
|
HSTS cache
The HTTP Strict-Transport-Security response header allows a website to signal that it should only be accessed via HTTPS. The browser remembers this directive in a database, but if this database is not partitioned, then it can be used to track users across websites."
| write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Used http, Used http unsupported: false, false passed: true, true test failed: false, false |
|
HSTS cache (fetch)
The HTTP Strict-Transport-Security response header allows a website to signal that it should only be accessed via HTTPS. The browser remembers this directive in a database, but if this database is not partitioned, then it can be used to track users across websites."
| write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested, not tested, not tested result, different first party: Used http, Used http, Used http, Used http, Used http unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Used http, Used http unsupported: false, false passed: true, true test failed: false, false |
|
iframe cache
An iframe is an element in a web page than allows websites to embed a second web page. Caching of this web page could be abused for cross-site tracking.
| write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|
|
image cache
Caching of images in web browsers is a standard behavior. But if that cache leaks between websites, it can be abused for cross-site tracking.
| write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2
result, different first party: 3, 3
unsupported: false, false
passed: true, true
test failed: false, false
|
|
indexedDB
The IndexedDB API exposes a transactional database to web pages. That database can be used to track users across websites, unless it is partitioned.
| write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same first party:
2867a272-3459-465c-88f5-c8c0601a7e8c,
a017c498-69a8-4e1c-bde8-188025a5fc0e,
843217a8-9a9f-4727-84fe-c9023de914c6,
c223fe48-a77f-4087-a844-72878ae074ce,
2f51ceb9-93b5-4f31-8fc6-26429268e18b
result, different first party: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same first party:
3381129f-ab1e-4185-897a-9961f05c5a89,
8a73aa6a-249f-456f-aa36-7d299a7f4bab,
aea7d626-1f84-4d5f-aa5f-f481621f7a77,
ec31548d-380e-405b-8425-e1f9a359fe1b,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234
result, different first party: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same first party:
e3162a38-9dff-4f7a-8c43-a42c5e945177,
d940278d-5bd6-490e-9de4-a5ce7c6e3346,
93972f1c-4b55-48e7-b36c-89c55f81499d,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3,
052bd326-3e1e-457e-a2a1-4de5bceb1764
result, different first party: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same first party:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f,
d648fea1-92c9-4677-98bc-37477e3a6935,
7c2ccb5a-8757-43b0-809c-92693c5728a8,
d6429a17-238b-4b71-8e41-ab16a05fd3e2,
8a22edfa-bdde-4576-aae0-5a5b2459fe17
result, different first party: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same first party:
81be7770-de26-46ba-a344-6855e2651f4e,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51,
b1fae351-a866-4214-a922-24e8eba8325e,
f27fcb7b-0656-432b-a99c-29f0069fc109,
52835348-3201-47b2-8505-8c648dbd0432
result, different first party: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same first party:
df336d08-f790-4b43-8c0d-f035748ce923,
71e9a742-0190-4ced-b3ae-474d3c125c49,
11bbfc32-cceb-4e06-96f1-f616876b2aa7,
f0b86440-e911-449e-b536-1cee79072fc3,
afb7ec45-db89-49ab-aacf-f3765763d32d
result, different first party: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same first party:
89fb71a3-e670-4f00-a55d-b90e9aed4261,
2938ddfd-f5a0-4245-b96d-e8c629a66498,
a8cbb18e-e019-4292-80b6-78528b4dae06,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe
result, different first party: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same first party:
45f08187-1804-4edc-8ea5-864c7aca7e23,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc,
592b7313-24d0-455a-b9c4-2bbafee9d271,
5d215dd1-0fc4-4f5e-a510-b649d179ba24
result, different first party: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same first party:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55,
f3bd2113-34b8-4cc3-802e-76d69472d962,
c240f51b-e239-4982-8f53-824fc630fb9f,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e,
64712c28-73a1-4e49-b42b-2c37152d98cb
result, different first party: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same first party:
8a2376aa-acdf-4f53-99e8-517a83b724eb,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5
result, different first party: undefined
unsupported: false, false
passed: true, true
test failed: false, false
|
|
localStorage
The localStorage API gives websites access to a key-value database that will remain available across visits. If the localStorage API is not partitioned or blocked, it can also be used to track users across websites.
| write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party:
2867a272-3459-465c-88f5-c8c0601a7e8c,
a017c498-69a8-4e1c-bde8-188025a5fc0e,
843217a8-9a9f-4727-84fe-c9023de914c6,
c223fe48-a77f-4087-a844-72878ae074ce,
2f51ceb9-93b5-4f31-8fc6-26429268e18b
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party:
3381129f-ab1e-4185-897a-9961f05c5a89,
8a73aa6a-249f-456f-aa36-7d299a7f4bab,
aea7d626-1f84-4d5f-aa5f-f481621f7a77,
ec31548d-380e-405b-8425-e1f9a359fe1b,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party:
e3162a38-9dff-4f7a-8c43-a42c5e945177,
d940278d-5bd6-490e-9de4-a5ce7c6e3346,
93972f1c-4b55-48e7-b36c-89c55f81499d,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3,
052bd326-3e1e-457e-a2a1-4de5bceb1764
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f,
d648fea1-92c9-4677-98bc-37477e3a6935,
7c2ccb5a-8757-43b0-809c-92693c5728a8,
d6429a17-238b-4b71-8e41-ab16a05fd3e2,
8a22edfa-bdde-4576-aae0-5a5b2459fe17
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party:
81be7770-de26-46ba-a344-6855e2651f4e,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51,
b1fae351-a866-4214-a922-24e8eba8325e,
f27fcb7b-0656-432b-a99c-29f0069fc109,
52835348-3201-47b2-8505-8c648dbd0432
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party:
df336d08-f790-4b43-8c0d-f035748ce923,
71e9a742-0190-4ced-b3ae-474d3c125c49,
11bbfc32-cceb-4e06-96f1-f616876b2aa7,
f0b86440-e911-449e-b536-1cee79072fc3,
afb7ec45-db89-49ab-aacf-f3765763d32d
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party:
89fb71a3-e670-4f00-a55d-b90e9aed4261,
2938ddfd-f5a0-4245-b96d-e8c629a66498,
a8cbb18e-e019-4292-80b6-78528b4dae06,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party:
45f08187-1804-4edc-8ea5-864c7aca7e23,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc,
592b7313-24d0-455a-b9c4-2bbafee9d271,
5d215dd1-0fc4-4f5e-a510-b649d179ba24
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55,
f3bd2113-34b8-4cc3-802e-76d69472d962,
c240f51b-e239-4982-8f53-824fc630fb9f,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e,
64712c28-73a1-4e49-b42b-2c37152d98cb
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party:
8a2376aa-acdf-4f53-99e8-517a83b724eb,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5
result, different first party: ,
unsupported: false, false
passed: true, true
test failed: false, false
|
|
locks
navigator.locks (only supported in some browsers) allows scripts on multiple tabs to coordinate. If this API is not partitioned, it can be used for cross-site tracking.
| write: async (key) => {
if (navigator.locks) {
navigator.locks.request(key, lock => new Promise((f,r) => {}));
let queryResult = await navigator.locks.query();
return queryResult.held[0].clientId;
} else {
throw new Error("Unsupported");
}
}
read: async () => {
if (navigator.locks) {
let queryResult = await navigator.locks.query();
return queryResult.held[0].name;
}
}
result, same first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
result, different first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (navigator.locks) {
navigator.locks.request(key, lock => new Promise((f,r) => {}));
let queryResult = await navigator.locks.query();
return queryResult.held[0].clientId;
} else {
throw new Error("Unsupported");
}
}
read: async () => {
if (navigator.locks) {
let queryResult = await navigator.locks.query();
return queryResult.held[0].name;
}
}
result, same first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
result, different first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (navigator.locks) {
navigator.locks.request(key, lock => new Promise((f,r) => {}));
let queryResult = await navigator.locks.query();
return queryResult.held[0].clientId;
} else {
throw new Error("Unsupported");
}
}
read: async () => {
if (navigator.locks) {
let queryResult = await navigator.locks.query();
return queryResult.held[0].name;
}
}
result, same first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
result, different first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (navigator.locks) {
navigator.locks.request(key, lock => new Promise((f,r) => {}));
let queryResult = await navigator.locks.query();
return queryResult.held[0].clientId;
} else {
throw new Error("Unsupported");
}
}
read: async () => {
if (navigator.locks) {
let queryResult = await navigator.locks.query();
return queryResult.held[0].name;
}
}
result, same first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
result, different first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (navigator.locks) {
navigator.locks.request(key, lock => new Promise((f,r) => {}));
let queryResult = await navigator.locks.query();
return queryResult.held[0].clientId;
} else {
throw new Error("Unsupported");
}
}
read: async () => {
if (navigator.locks) {
let queryResult = await navigator.locks.query();
return queryResult.held[0].name;
}
}
result, same first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
result, different first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (navigator.locks) {
navigator.locks.request(key, lock => new Promise((f,r) => {}));
let queryResult = await navigator.locks.query();
return queryResult.held[0].clientId;
} else {
throw new Error("Unsupported");
}
}
read: async () => {
if (navigator.locks) {
let queryResult = await navigator.locks.query();
return queryResult.held[0].name;
}
}
result, same first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
result, different first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (navigator.locks) {
navigator.locks.request(key, lock => new Promise((f,r) => {}));
let queryResult = await navigator.locks.query();
return queryResult.held[0].clientId;
} else {
throw new Error("Unsupported");
}
}
read: async () => {
if (navigator.locks) {
let queryResult = await navigator.locks.query();
return queryResult.held[0].name;
}
}
result, same first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
result, different first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (navigator.locks) {
navigator.locks.request(key, lock => new Promise((f,r) => {}));
let queryResult = await navigator.locks.query();
return queryResult.held[0].clientId;
} else {
throw new Error("Unsupported");
}
}
read: async () => {
if (navigator.locks) {
let queryResult = await navigator.locks.query();
return queryResult.held[0].name;
}
}
result, same first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
result, different first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (navigator.locks) {
navigator.locks.request(key, lock => new Promise((f,r) => {}));
let queryResult = await navigator.locks.query();
return queryResult.held[0].clientId;
} else {
throw new Error("Unsupported");
}
}
read: async () => {
if (navigator.locks) {
let queryResult = await navigator.locks.query();
return queryResult.held[0].name;
}
}
result, same first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
result, different first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (navigator.locks) {
navigator.locks.request(key, lock => new Promise((f,r) => {}));
let queryResult = await navigator.locks.query();
return queryResult.held[0].clientId;
} else {
throw new Error("Unsupported");
}
}
read: async () => {
if (navigator.locks) {
let queryResult = await navigator.locks.query();
return queryResult.held[0].name;
}
}
result, same first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
result, different first party:
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
unsupported: false, false
passed: undefined
test failed: true, true
|
|
prefetch cache
A <link rel='prefetch'...> suggests to browsers they should fetch a resource ahead of time and cache it. But if browsers don't partition this cache, it can be used to track users across websites.
| write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same first party:
Error: No requests received,
Error: No requests received
result, different first party:
Error: No requests received,
Error: No requests received
unsupported: true, true
passed: undefined
test failed: false, false
|
|
script cache
Caching of scripts in web browsers is a standard behavior. But if that cache leaks between websites, it can be abused for cross-site tracking.
| write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same first party: 2, 2
result, different first party: 3, 3
unsupported: false, false
passed: true, true
test failed: false, false
|
|
ServiceWorker
The ServiceWorker API allows websites to run code in the background and store content in the browser for offline use. If a ServiceWorker can be accessed from multiple websites, it can be abused to track users across sites.
| write: async (key) => {
if (!navigator.serviceWorker) {
throw new Error("Unsupported");
}
let registration = await navigator.serviceWorker.register(
'serviceWorker.js');
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
await fetch(`serviceworker-write?secret=${key}`);
}
read: async () => {
console.log("trying to register the serviceworker now...");
const registration = await Promise.race([
navigator.serviceWorker.register('serviceWorker.js'),
sleepMs(500)
]);
if (registration === undefined) {
// We timed out or otherwise failed.
throw new Error("ServiceWorker registration failed");
}
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
let response = await fetch("serviceworker-read");
return await response.text();
}
result, same first party: , , , ,
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (!navigator.serviceWorker) {
throw new Error("Unsupported");
}
let registration = await navigator.serviceWorker.register(
'serviceWorker.js');
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
await fetch(`serviceworker-write?secret=${key}`);
}
read: async () => {
console.log("trying to register the serviceworker now...");
const registration = await Promise.race([
navigator.serviceWorker.register('serviceWorker.js'),
sleepMs(500)
]);
if (registration === undefined) {
// We timed out or otherwise failed.
throw new Error("ServiceWorker registration failed");
}
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
let response = await fetch("serviceworker-read");
return await response.text();
}
result, same first party: , , , ,
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (!navigator.serviceWorker) {
throw new Error("Unsupported");
}
let registration = await navigator.serviceWorker.register(
'serviceWorker.js');
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
await fetch(`serviceworker-write?secret=${key}`);
}
read: async () => {
console.log("trying to register the serviceworker now...");
const registration = await Promise.race([
navigator.serviceWorker.register('serviceWorker.js'),
sleepMs(500)
]);
if (registration === undefined) {
// We timed out or otherwise failed.
throw new Error("ServiceWorker registration failed");
}
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
let response = await fetch("serviceworker-read");
return await response.text();
}
result, same first party: , , , ,
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (!navigator.serviceWorker) {
throw new Error("Unsupported");
}
let registration = await navigator.serviceWorker.register(
'serviceWorker.js');
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
await fetch(`serviceworker-write?secret=${key}`);
}
read: async () => {
console.log("trying to register the serviceworker now...");
const registration = await Promise.race([
navigator.serviceWorker.register('serviceWorker.js'),
sleepMs(500)
]);
if (registration === undefined) {
// We timed out or otherwise failed.
throw new Error("ServiceWorker registration failed");
}
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
let response = await fetch("serviceworker-read");
return await response.text();
}
result, same first party: , , , ,
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (!navigator.serviceWorker) {
throw new Error("Unsupported");
}
let registration = await navigator.serviceWorker.register(
'serviceWorker.js');
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
await fetch(`serviceworker-write?secret=${key}`);
}
read: async () => {
console.log("trying to register the serviceworker now...");
const registration = await Promise.race([
navigator.serviceWorker.register('serviceWorker.js'),
sleepMs(500)
]);
if (registration === undefined) {
// We timed out or otherwise failed.
throw new Error("ServiceWorker registration failed");
}
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
let response = await fetch("serviceworker-read");
return await response.text();
}
result, same first party: , , , ,
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (!navigator.serviceWorker) {
throw new Error("Unsupported");
}
let registration = await navigator.serviceWorker.register(
'serviceWorker.js');
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
await fetch(`serviceworker-write?secret=${key}`);
}
read: async () => {
console.log("trying to register the serviceworker now...");
const registration = await Promise.race([
navigator.serviceWorker.register('serviceWorker.js'),
sleepMs(500)
]);
if (registration === undefined) {
// We timed out or otherwise failed.
throw new Error("ServiceWorker registration failed");
}
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
let response = await fetch("serviceworker-read");
return await response.text();
}
result, same first party: , , , ,
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (!navigator.serviceWorker) {
throw new Error("Unsupported");
}
let registration = await navigator.serviceWorker.register(
'serviceWorker.js');
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
await fetch(`serviceworker-write?secret=${key}`);
}
read: async () => {
console.log("trying to register the serviceworker now...");
const registration = await Promise.race([
navigator.serviceWorker.register('serviceWorker.js'),
sleepMs(500)
]);
if (registration === undefined) {
// We timed out or otherwise failed.
throw new Error("ServiceWorker registration failed");
}
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
let response = await fetch("serviceworker-read");
return await response.text();
}
result, same first party: , , , ,
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (!navigator.serviceWorker) {
throw new Error("Unsupported");
}
let registration = await navigator.serviceWorker.register(
'serviceWorker.js');
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
await fetch(`serviceworker-write?secret=${key}`);
}
read: async () => {
console.log("trying to register the serviceworker now...");
const registration = await Promise.race([
navigator.serviceWorker.register('serviceWorker.js'),
sleepMs(500)
]);
if (registration === undefined) {
// We timed out or otherwise failed.
throw new Error("ServiceWorker registration failed");
}
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
let response = await fetch("serviceworker-read");
return await response.text();
}
result, same first party: , , , ,
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (!navigator.serviceWorker) {
throw new Error("Unsupported");
}
let registration = await navigator.serviceWorker.register(
'serviceWorker.js');
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
await fetch(`serviceworker-write?secret=${key}`);
}
read: async () => {
console.log("trying to register the serviceworker now...");
const registration = await Promise.race([
navigator.serviceWorker.register('serviceWorker.js'),
sleepMs(500)
]);
if (registration === undefined) {
// We timed out or otherwise failed.
throw new Error("ServiceWorker registration failed");
}
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
let response = await fetch("serviceworker-read");
return await response.text();
}
result, same first party: , , , ,
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: undefined
test failed: true, true, true, true, true
|
write: async (key) => {
if (!navigator.serviceWorker) {
throw new Error("Unsupported");
}
let registration = await navigator.serviceWorker.register(
'serviceWorker.js');
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
await fetch(`serviceworker-write?secret=${key}`);
}
read: async () => {
console.log("trying to register the serviceworker now...");
const registration = await Promise.race([
navigator.serviceWorker.register('serviceWorker.js'),
sleepMs(500)
]);
if (registration === undefined) {
// We timed out or otherwise failed.
throw new Error("ServiceWorker registration failed");
}
console.log(registration);
await navigator.serviceWorker.ready;
console.log("service worker ready");
await sleepMs(100);
let response = await fetch("serviceworker-read");
return await response.text();
}
result, same first party: ,
result, different first party: ,
unsupported: false, false
passed: undefined
test failed: true, true
|
|
SharedWorker
The SharedWorker API allows scripts from multiple tabs to share a background thread of computation. If SharedWorker is not partitioned, then it can be abused to shared data between websites in your browser.
| write: async (secret) => {
try {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
// console.log("worker", worker);
const messagePromise = new Promise((resolve) => {
worker.port.onmessage = (e) => resolve(e.data);
});
worker.port.postMessage(secret);
await messagePromise;
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
const messagePromise = new Promise((resolve, reject) => {
worker.port.onmessage = (e) => resolve(e.data);
setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
});
worker.port.postMessage("request");
const message = await messagePromise;
if (message === "none") {
throw new Error("Unsupported");
}
return message;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
// console.log("worker", worker);
const messagePromise = new Promise((resolve) => {
worker.port.onmessage = (e) => resolve(e.data);
});
worker.port.postMessage(secret);
await messagePromise;
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
const messagePromise = new Promise((resolve, reject) => {
worker.port.onmessage = (e) => resolve(e.data);
setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
});
worker.port.postMessage("request");
const message = await messagePromise;
if (message === "none") {
throw new Error("Unsupported");
}
return message;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
// console.log("worker", worker);
const messagePromise = new Promise((resolve) => {
worker.port.onmessage = (e) => resolve(e.data);
});
worker.port.postMessage(secret);
await messagePromise;
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
const messagePromise = new Promise((resolve, reject) => {
worker.port.onmessage = (e) => resolve(e.data);
setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
});
worker.port.postMessage("request");
const message = await messagePromise;
if (message === "none") {
throw new Error("Unsupported");
}
return message;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
// console.log("worker", worker);
const messagePromise = new Promise((resolve) => {
worker.port.onmessage = (e) => resolve(e.data);
});
worker.port.postMessage(secret);
await messagePromise;
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
const messagePromise = new Promise((resolve, reject) => {
worker.port.onmessage = (e) => resolve(e.data);
setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
});
worker.port.postMessage("request");
const message = await messagePromise;
if (message === "none") {
throw new Error("Unsupported");
}
return message;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
// console.log("worker", worker);
const messagePromise = new Promise((resolve) => {
worker.port.onmessage = (e) => resolve(e.data);
});
worker.port.postMessage(secret);
await messagePromise;
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
const messagePromise = new Promise((resolve, reject) => {
worker.port.onmessage = (e) => resolve(e.data);
setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
});
worker.port.postMessage("request");
const message = await messagePromise;
if (message === "none") {
throw new Error("Unsupported");
}
return message;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
// console.log("worker", worker);
const messagePromise = new Promise((resolve) => {
worker.port.onmessage = (e) => resolve(e.data);
});
worker.port.postMessage(secret);
await messagePromise;
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
const messagePromise = new Promise((resolve, reject) => {
worker.port.onmessage = (e) => resolve(e.data);
setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
});
worker.port.postMessage("request");
const message = await messagePromise;
if (message === "none") {
throw new Error("Unsupported");
}
return message;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
// console.log("worker", worker);
const messagePromise = new Promise((resolve) => {
worker.port.onmessage = (e) => resolve(e.data);
});
worker.port.postMessage(secret);
await messagePromise;
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
const messagePromise = new Promise((resolve, reject) => {
worker.port.onmessage = (e) => resolve(e.data);
setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
});
worker.port.postMessage("request");
const message = await messagePromise;
if (message === "none") {
throw new Error("Unsupported");
}
return message;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
// console.log("worker", worker);
const messagePromise = new Promise((resolve) => {
worker.port.onmessage = (e) => resolve(e.data);
});
worker.port.postMessage(secret);
await messagePromise;
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
const messagePromise = new Promise((resolve, reject) => {
worker.port.onmessage = (e) => resolve(e.data);
setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
});
worker.port.postMessage("request");
const message = await messagePromise;
if (message === "none") {
throw new Error("Unsupported");
}
return message;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
// console.log("worker", worker);
const messagePromise = new Promise((resolve) => {
worker.port.onmessage = (e) => resolve(e.data);
});
worker.port.postMessage(secret);
await messagePromise;
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
const messagePromise = new Promise((resolve, reject) => {
worker.port.onmessage = (e) => resolve(e.data);
setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
});
worker.port.postMessage("request");
const message = await messagePromise;
if (message === "none") {
throw new Error("Unsupported");
}
return message;
}
result, same first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
// console.log("worker", worker);
const messagePromise = new Promise((resolve) => {
worker.port.onmessage = (e) => resolve(e.data);
});
worker.port.postMessage(secret);
await messagePromise;
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let worker = new SharedWorker("supercookies_sharedworker.js");
worker.port.start();
const messagePromise = new Promise((resolve, reject) => {
worker.port.onmessage = (e) => resolve(e.data);
setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
});
worker.port.postMessage("request");
const message = await messagePromise;
if (message === "none") {
throw new Error("Unsupported");
}
return message;
}
result, same first party:
Error: Unsupported,
Error: Unsupported
result, different first party:
Error: Unsupported,
Error: Unsupported
unsupported: true, true
passed: undefined
test failed: false, false
|
|
TLS Session ID
The TLS protocol is used by HTTPS to make connections secure. If the browser were to re-use a TLS session, then the session ID could be used to track users across websites.
| write: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
read: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
result, same first party:
274af3577de57e3f94301f3d7fac0f45311e4ac353ba8127abc8f5acc4a2fadf,
6b788aff97094c59e3d4747de3b528e629f71b96ecf98ba7b3c3175705730721,
3f31b35c968253c53d94a715e630a9be018fb97a7c1d023db0669d0238896bc4,
cfcf1e32c1675645283cec2985855545b84dc1bb701da6f28b63a691bca9a4c0,
a48ed6f4cd37af8d27372919c66675b66d6619cc95cc8a26260d86320cf0af2d
result, different first party:
e9f3b0df47c1ab21a9da1df8bcf267e43068b59d158b8670904b80c5e3b9008d,
7aa7b1b01c494a0764b28635703d6a78f616c83e0fcdb338618fb754ce3efdf6,
46a635142fde99833b004bad0d3e3aae5274e5b6d415840bb66cc0c51f72c4e2,
95f856fe425f551f62b6458e3867e5a72b637220c6d7984ce75fcc92fdfa2f12,
df0c99ca3cb7d4dbc5ac055c4bfdcd651b3331466c4cbfb7f6ad6567a13a4948
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
read: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
result, same first party:
821e6ff95911eb2dc0d2590f28aeb97d9fabe16cc3668a10a660a011da1da790,
06073c328169834892f1c7dfd31837104b723734696ece2228d7156dcc9875a3,
e2bb6f216872b07bea40e48fe41b674a0db5d04b929d0acda9db73573e7a5dba,
24cc0862a3a4efe05b50996f79634d81fcbb520f4bea951c37bf72e091274742,
fb50e19b3b3415761f467555b3e98205211dc373287bcc5ca1d2b7bdb3506773
result, different first party:
69707ce5550b8026768e1268bb76930fec30b7a552db4930713f6ac85e3839f2,
c5abeeefda64f9ad52fa7e9df9cc5bf8154b23b95a29e94c96f49e9c233be5a4,
70088e0f6c78de8d837efdbc442d7b2d328fdd00509bf62b0943d6a7cb828ea3,
691277d43325e31e0d38cf4f46d6feef43cd189a74755521b963dbf1640027b6,
cd21f1b4cbef17ebb28433d681ad454fa5d9cc92701b0897a2c84724a991d660
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
read: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
result, same first party:
89a7fd43f50a806c7599fe359657fe95460bf94b8c4dbb66b6727e0fdb194eee,
f4485ac0ae9cf5b7bcdbbe1dc6e2f1b98d3724c5c4186341c84d8ea6a56a64e7,
9328b5b3b8c22c7545b7144ed29adb4fc285cac148fe8c2fcde2a8afc0f833bb,
8101f194eac570667b9bc03912f7b40a0cbfa29ddb2b1c66279e0ce6225681e3,
eba1f62ce1d012f29122e43fd0c51fbb272e4cf779e97d76034d6f25eadde860
result, different first party:
67d3127c3a2daf6e7bc9940732bd8abaab9b74d374725f188c1c62a663acb838,
83014c6fff14721f629a146d5d406d0ac26256dfb8014fae16c79ea42e1246cb,
1033f71dd497ffff2588f82ad6243d652fcd2833bca6b2eefa80bb71c119df4c,
a62110a001e908ac0ba179d103a86a535c1d1567b556f67924827deaa3b26a51,
494671ef65aa9254303098102d2cc0d3e6583f390d38eb246a1baf98f0170722
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
read: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
result, same first party:
d17ec9d0161f9d834067c6b837eb5c099cc75bd2fab654568df2a75cd6f84298,
d9949fe605353fdc931650620830b4e3bca5203c470589e24e1dffc2ee7f8d1d,
6173c3ebd9a4e43236d36720dceaa7cd4e1d8dcc79e313fda746a42f87e02593,
3f076651ef48317cc8ea39779115c427d3d2d0cbdb081dd25591400a7ecb0111,
6fa8869825b8f43e1650a4f7755244f904cf6a51ba7af68f36c282fb3f5cab22
result, different first party:
3aa78339f3d871d9afc11eec062d9c6ddada7a5b9cb0a0bd447b579c1cbbb6a6,
0829c4ec1ca3ce54308dd92c5f0f5fb06b3bd114a152466f14679756d3be566c,
a4582bee54a61ed659b30cc210cdd10703662e533456da1ee5d6e7a2ab22a09a,
fb47dff15fd4bae599571478bc251f0a8666c4bbbad85d5fdd9f6d50494142ca,
ee9939171b7309ae860ca42ff91d4ce9f6d824da762c1f3e85caf1432c4e406a
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
read: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
result, same first party:
ff47575a006625a6bc2faafe5bea76c33b281795fee16a3ac99b0f026ef6c366,
d2481eb826e16e4f9adf6f691ae0563d6119ecf5cd8670b986d7ad0844cd27a6,
ede12f5a388c70cfadff41aa2f7ac0c3593a390c95d25d43a14043cc30a3024c,
a4c1b3c764ec61017cbeec258347b54626bab9bfcb6141a3ba305462d6b4ac32,
f681366e34b8b0922ab716a8a6dc38c0ece780ebe840fcd0836d962f9d9be821
result, different first party:
ec9b6ed89ef85c7b4838d1e5a56bb5e9f9e6ba3d99993970e15015b757aaf2c0,
3705c206299c43fd7ae09f48e6419c50316d2b8d0cd757034a1c42a880cc1cf4,
3f8d37205573f44dc290d87a0b08577cee0fbd79aaf637633485cf5c5a5373b5,
67ad9ec095f861e235862a4253fc005ede24ed6a8766c3bab0dc2cc82ddfdfa9,
bc41732339f8d9c3e435add38bff69f893e356118f7f2feb7fe7b5980651f295
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
read: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
result, same first party:
9b87b04f4aa4302e24c143c18e81056288e8074b91ebcf42636d2525a34174ad,
b2fc0ce1ecb2f25e053ed91615d810da12462cd8d5af42132ff415997774819d,
72f8d0e2b1e036cff8934a0ee6946be3106101ce140d83138ff34355096ac968,
bbaf9cdd82ea107068ddf227b461c50c82fb9c785079fa1b8758ee8a489d310c,
7d3a9f4b5fd04fb7793a8a81d4325e7ba5cbb00d49ed3f5c3e2c93682e27eab9
result, different first party:
588d0ca8548231d15cc141c9e6ff8733ef7311f6c41e2d95fde2f37164d1d785,
7f449b3e3a3be4a4e025b44c4975f95643b5e69e44ee16529a4993371bdbc90a,
f4d7b8a20525a2ab4213051daaabc5eb4263057ae8da83f69ef21162fb096144,
0a1bc3f3dfbb40d2bd3524766a48a2b6b2156f685c91ba91815774eeaffae65e,
1ed2f7d8fb347d32c2ee1013ec86788dd0f19783ebaadc4bd9659bed1fcffd02
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
read: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
result, same first party:
cfefd5185b24591634b2ab4e7ff146970e7c9b4149b5c110dc91c24d9552d97d,
16bd9b31e1c67a73beffc97d4bfec99f300ca039be5dc7478a840cc073efca1a,
801e5358f48204cc989286415fd4b4218f7ea83bcaa07e9da517dc5735929baf,
6d2d8a7fca46175cc102c7ee7c059c7b24880b072ce814f9444d23897fdaf7a7,
48330dffda71f8308b045875c886d77610fae7ae53c9a8bb24c4693ad4ad4710
result, different first party:
362eb3d922cfccb5112925bb7004aad7c8938334d20565a016b17efb6cb9f2ea,
5b70008f6be9ce658fcafb3b9f46229949049d3d73f283015db9c0f950f84ea2,
142b69efec2cdeebc39e066783e1dc5975cc8dc9078623ad006040fe0afb2d9e,
0856e4ca812911ba271e861c95fafdde7c6f9f04675675e09c4fc74c75e34223,
475b4714b503eb1fb0a7c39418947b9cd46984498ad501ecdae23d7660e03859
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
read: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
result, same first party:
cf4f21d31aef5509ee6f731251b11eae7378028d5e3e6f7d86e3eb30d93f56f4,
2055841ac67736cd39e0103ccc3837b5336acc26aaea02e6e8e285a6b8c94344,
2d5f4c0bfd80d7533f9d96a2180757d27c7562e74abc2a127f173ce7a684436d,
76b3617ccedbc901e7fa5ef582d6c84f5642540d87867435fda0a058e4089c8b,
06238b191d02579aee1d110231961f7034f87dc4691f7eb21fd1d4e54f327331
result, different first party:
fc2279874a894277d183465b4e13027bf9b20647b83102f6da36041677ec2a34,
45e71a561af8365a01a8bad8c46a4dcb77c575594d7009f7d3ad650a332c0225,
eacdacb3f64256474e4d672a028225ea33c21a7e4b558d6a96861e0630602bd2,
470b8d8c96700898a21f622a22b19188de04101e993354c85b8699cf9f4dc413,
28cf49d6e77166ff549a47af2a6175e5f4cc564995c829666152986ddc35626b
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
read: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
result, same first party:
2cbec12574281f96c2777276a39c7b70afd9ae705b9e3e718a7441b4c7a95625,
785bf3571d61014633b5c66f3fd9d5bc4fb79ecf5270acd260a01342363d748a,
22c8f1db0deea48cec5403f55f113570ab495728f3cf0dfc59a1608c9bc4026d,
ebcc328568ffe136e845e6ce6417337582217231401fd0415e55b0f50282b44c,
2e88adc07ae05e21c2879688cc0500f5f4adbee1127ff9e3268f53f73ed6d065
result, different first party:
2f7b33e3673a9dc80443681798f8d5897708692d1f6775117e881499cc76e442,
2483d1b53008a1990a7f7b33ae53fcf09088ec98216c3b4356be61e8f670fd87,
3b03ffcca0104d477b64807fcd87ac0bebc3cb7c894734898221fd57606a880d,
2c884d1733566a85a81045313022d7234241f811d4945b94976bbaad2557b59b,
5ae737d76e74b2e796b07ab7eafe01af52b5ffb600c8ecac8b13b37eb0646662
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
read: async () => {
let results = await fetch("https://tls.privacytests2.org:8900/");
return (await results.json()).sessionId;
}
result, same first party:
ea35a0fe04375ce357e28c4ca105ab24b586c24c0f19f568a10dcae15990588d,
9192a31fe16d31d81afa04903d1c27eac377f80046f7a75f2b66c53b1c4fe795
result, different first party:
da9e4beaab12703db0b93064bf0cd4b0627a154acb8de9bfce11646b425eadb8,
1cd2c4b778a630df809e35ebdf8045127c6511448aba339aa1b5a495c8014bf6
unsupported: false, false
passed: true, true
test failed: false, false
|
|
XMLHttpRequest cache
Similar to the newer Fetch API, any resource received may be cached by the browser. The cache is potentially vulnerable to cross-site tracking attack.
| write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 2, 2, 2, 2, 2
result, different first party: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 1, 1, 1, 1, 1
result, different first party: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same first party: 2, 2
result, different first party: 3, 3
unsupported: false, false
passed: true, true
test failed: false, false
|
|
Navigation testsWhich browsers prevent websites from sharing tracking data when you click on a link?
When you click a hyperlink to navigate your browser from one site to another, certain browser APIs allow the first site to communicate to the second site. These privacy vulnerabilities can be fixed by introducing new limits on how much data is transfered between sites.',
| ||||||||||
|
document.referrer
The Referer [sic] request header is a mechanism used by browsers to let a website know where the user is visiting from. This header is inherently tracking users across websites. In recent times, browsers have switched to a policy of trimming a referrer to convey less tracking information, but Referer continues to convey cross-site tracking data by default.
| write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party:
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false
passed: false, false
test failed: false, false
|
|
sessionStorage
The sessionStorage API is similar to the localStorage API, but it does not persist across tabs or across browser sessions. Nonetheless, it can be used to track users if they navigate from one website to another. This tracking can be thwarted by partitioning sessionStorage between websites.
| write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party:
2867a272-3459-465c-88f5-c8c0601a7e8c,
a017c498-69a8-4e1c-bde8-188025a5fc0e,
843217a8-9a9f-4727-84fe-c9023de914c6,
c223fe48-a77f-4087-a844-72878ae074ce,
2f51ceb9-93b5-4f31-8fc6-26429268e18b
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party:
3381129f-ab1e-4185-897a-9961f05c5a89,
8a73aa6a-249f-456f-aa36-7d299a7f4bab,
aea7d626-1f84-4d5f-aa5f-f481621f7a77,
ec31548d-380e-405b-8425-e1f9a359fe1b,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party:
e3162a38-9dff-4f7a-8c43-a42c5e945177,
d940278d-5bd6-490e-9de4-a5ce7c6e3346,
93972f1c-4b55-48e7-b36c-89c55f81499d,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3,
052bd326-3e1e-457e-a2a1-4de5bceb1764
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f,
d648fea1-92c9-4677-98bc-37477e3a6935,
7c2ccb5a-8757-43b0-809c-92693c5728a8,
d6429a17-238b-4b71-8e41-ab16a05fd3e2,
8a22edfa-bdde-4576-aae0-5a5b2459fe17
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party:
81be7770-de26-46ba-a344-6855e2651f4e,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51,
b1fae351-a866-4214-a922-24e8eba8325e,
f27fcb7b-0656-432b-a99c-29f0069fc109,
52835348-3201-47b2-8505-8c648dbd0432
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party:
df336d08-f790-4b43-8c0d-f035748ce923,
71e9a742-0190-4ced-b3ae-474d3c125c49,
11bbfc32-cceb-4e06-96f1-f616876b2aa7,
f0b86440-e911-449e-b536-1cee79072fc3,
afb7ec45-db89-49ab-aacf-f3765763d32d
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party:
89fb71a3-e670-4f00-a55d-b90e9aed4261,
2938ddfd-f5a0-4245-b96d-e8c629a66498,
a8cbb18e-e019-4292-80b6-78528b4dae06,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party:
45f08187-1804-4edc-8ea5-864c7aca7e23,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc,
592b7313-24d0-455a-b9c4-2bbafee9d271,
5d215dd1-0fc4-4f5e-a510-b649d179ba24
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55,
f3bd2113-34b8-4cc3-802e-76d69472d962,
c240f51b-e239-4982-8f53-824fc630fb9f,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e,
64712c28-73a1-4e49-b42b-2c37152d98cb
result, different first party: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party:
8a2376aa-acdf-4f53-99e8-517a83b724eb,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5
result, different first party: ,
unsupported: false, false
passed: true, true
test failed: false, false
|
|
window.name
The window.name API allows websites to store data that will persist after the user has navigated the tab to a different website. This mechanism could be partitioned so that data is not allowed to persist between websites.
| write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_2867a272-3459-465c-88f5-c8c0601a7e8c, name_a017c498-69a8-4e1c-bde8-188025a5fc0e, name_843217a8-9a9f-4727-84fe-c9023de914c6, name_c223fe48-a77f-4087-a844-72878ae074ce, name_2f51ceb9-93b5-4f31-8fc6-26429268e18b result, different first party: , , , , unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_3381129f-ab1e-4185-897a-9961f05c5a89, name_8a73aa6a-249f-456f-aa36-7d299a7f4bab, name_aea7d626-1f84-4d5f-aa5f-f481621f7a77, name_ec31548d-380e-405b-8425-e1f9a359fe1b, name_1b57d08d-9b09-4e8a-8cdd-b2aaa0840234 result, different first party: , , , , unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_e3162a38-9dff-4f7a-8c43-a42c5e945177, name_d940278d-5bd6-490e-9de4-a5ce7c6e3346, name_93972f1c-4b55-48e7-b36c-89c55f81499d, name_aa9e5dc5-1cc7-410e-9b63-5186f464c3e3, name_052bd326-3e1e-457e-a2a1-4de5bceb1764 result, different first party: , , , , unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_e62c4bb5-60f7-4a55-8f60-8e4a5158c69f, name_d648fea1-92c9-4677-98bc-37477e3a6935, name_7c2ccb5a-8757-43b0-809c-92693c5728a8, name_d6429a17-238b-4b71-8e41-ab16a05fd3e2, name_8a22edfa-bdde-4576-aae0-5a5b2459fe17 result, different first party: , , , , unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_81be7770-de26-46ba-a344-6855e2651f4e, name_dcd9e4c9-1c9b-4533-8c44-08289a6a0d51, name_b1fae351-a866-4214-a922-24e8eba8325e, name_f27fcb7b-0656-432b-a99c-29f0069fc109, name_52835348-3201-47b2-8505-8c648dbd0432 result, different first party: , , , , unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_df336d08-f790-4b43-8c0d-f035748ce923, name_71e9a742-0190-4ced-b3ae-474d3c125c49, name_11bbfc32-cceb-4e06-96f1-f616876b2aa7, name_f0b86440-e911-449e-b536-1cee79072fc3, name_afb7ec45-db89-49ab-aacf-f3765763d32d result, different first party: , , , , unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_89fb71a3-e670-4f00-a55d-b90e9aed4261, name_2938ddfd-f5a0-4245-b96d-e8c629a66498, name_a8cbb18e-e019-4292-80b6-78528b4dae06, name_f664c8aa-3054-4d72-a2c0-c1ff1f8fda24, name_22eafbfd-c5a4-4816-b3ef-a8f5011629fe result, different first party: , , , , unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_45f08187-1804-4edc-8ea5-864c7aca7e23, name_f081a313-3b2d-4a8b-ae32-7bd7784ea0cc, name_02603275-d817-4ad3-b03c-0aaf1c6f6bfc, name_592b7313-24d0-455a-b9c4-2bbafee9d271, name_5d215dd1-0fc4-4f5e-a510-b649d179ba24 result, different first party: , , , , unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55, name_f3bd2113-34b8-4cc3-802e-76d69472d962, name_c240f51b-e239-4982-8f53-824fc630fb9f, name_3f271bd4-c40c-4c60-b4a8-6bba65e1123e, name_64712c28-73a1-4e49-b42b-2c37152d98cb result, different first party: , , , , unsupported: false, false, false, false, false passed: true, true, true, true, true test failed: false, false, false, false, false |
write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_8a2376aa-acdf-4f53-99e8-517a83b724eb, name_c279cb7e-5a12-4ef7-a175-6d79595fb2f5 result, different first party: , unsupported: false, false passed: true, true test failed: false, false |
|
HTTPS testsWhich browsers prevent unencrypted network connections?
HTTPS is the protocol that web browsers use to connect securely to websites. When HTTPS is being used, the connection is encrypted so that third parties on the network cannot read content being sent between the server and your browser. In the past, insecure connections were the default and websites would need to actively request that a browser use HTTPS. Now the status quo is shifting, and browser makers are moving toward a world where HTTPS is the default protocol.`
| ||||||||||
|
Insecure website warning
Checks to see if the browser stops loading an insecure website and warns the user before giving them the option to continue. Known as HTTPS-Only Mode in some browsers.
| passed: false, false, false, false, false result: Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded |
passed: false, false, false, false, false result: Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded |
passed: false, false, false, false, false result: Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded |
passed: false, false, false, false, false result: Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded |
passed: false, false, false, false, false result: Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded |
passed: false, false, false, false, false result: Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded |
passed: false, false, false, false, false result: Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded |
passed: false, false, false, false, false result: Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded |
passed: false, false, false, false, false result: Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded, Insecure website loaded |
passed: false, false result: Insecure website loaded, Insecure website loaded |
|
Upgradable address
Checks to see if an insecure address entered into the browser's address bar is upgraded to HTTPS whenever possible.
| upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false passed: false, false |
|
Upgradable hyperlink
Checks to see if the user has clicked on a hyperlink to an insecure address, if the browser upgrades that address to HTTPS whenever possible.
| upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false, false, false, false passed: false, false, false, false, false |
upgraded: false, false passed: false, false |
|
Upgradable image
Checks to see if the browser attempts to upgrade an insecure address for an image to HTTPS whenever possible.
| passed: true, true, true, true, true result: upgraded, upgraded, upgraded, upgraded, upgraded |
passed: true, true, true, true, true result: upgraded, upgraded, upgraded, upgraded, upgraded |
passed: false, false, false, false, false result: loaded insecurely, loaded insecurely, loaded insecurely, loaded insecurely, loaded insecurely |
passed: true, true, true, true, true result: upgraded, upgraded, upgraded, upgraded, upgraded |
passed: false, false, false, false, false result: loaded insecurely, loaded insecurely, loaded insecurely, loaded insecurely, loaded insecurely |
passed: false, false, false, false, false result: loaded insecurely, loaded insecurely, loaded insecurely, loaded insecurely, loaded insecurely |
passed: false, false, false, false, false result: loaded insecurely, loaded insecurely, loaded insecurely, loaded insecurely, loaded insecurely |
passed: false, false, false, false, false result: loaded insecurely, loaded insecurely, loaded insecurely, loaded insecurely, loaded insecurely |
passed: true, true, true, true, true result: upgraded, upgraded, upgraded, upgraded, upgraded |
passed: false, false result: loaded insecurely, loaded insecurely |
|
Upgradable script
Checks to see if the browser attempts to upgrade an insecure address for an script to HTTPS whenever possible.
| passed: true, true, true, true, true result: blocked, blocked, blocked, blocked, blocked |
passed: true, true, true, true, true result: blocked, blocked, blocked, blocked, blocked |
passed: true, true, true, true, true result: blocked, blocked, blocked, blocked, blocked |
passed: true, true, true, true, true result: blocked, blocked, blocked, blocked, blocked |
passed: true, true, true, true, true result: blocked, blocked, blocked, blocked, blocked |
passed: true, true, true, true, true result: blocked, blocked, blocked, blocked, blocked |
passed: true, true, true, true, true result: blocked, blocked, blocked, blocked, blocked |
passed: true, true, true, true, true result: blocked, blocked, blocked, blocked, blocked |
passed: true, true, true, true, true result: blocked, blocked, blocked, blocked, blocked |
passed: true, true result: blocked, blocked |
|
Misc testsWhich browsers provide additional assorted privacy protections?
This category includes tests for the presence of miscellaneous privacy features
| ||||||||||
|
ECH enabled
Encrypted Client Hello (ECH) is a new protocol that hides the website you are visiting from third-party network eavesdroppers.
| SNI_status: plaintext passed: false, false, false, false, false |
SNI_status: plaintext passed: false, false, false, false, false |
SNI_status: plaintext passed: false, false, false, false, false |
SNI_status: plaintext passed: false, false, false, false, false |
SNI_status: plaintext passed: false, false, false, false, false |
SNI_status: plaintext passed: false, false, false, false, false |
SNI_status: plaintext passed: false, false, false, false, false |
SNI_status: plaintext passed: false, false, false, false, false |
SNI_status: plaintext passed: false, false, false, false, false |
SNI_status: plaintext passed: false, false |
|
GPC enabled first-party
The Global Privacy Control is an HTTP header that can be sent by a browser to instruct a website not to sell the user's personal data to third parties. This test checks to see if the GPC header is sent by default to the top-level website.
| header value: undefined passed: false, false, false, false, false |
header value: undefined passed: false, false, false, false, false |
header value: undefined passed: false, false, false, false, false |
header value: undefined passed: false, false, false, false, false |
header value: undefined passed: false, false, false, false, false |
header value: undefined passed: false, false, false, false, false |
header value: undefined passed: false, false, false, false, false |
header value: undefined passed: false, false, false, false, false |
header value: undefined passed: false, false, false, false, false |
header value: undefined passed: false, false |
|
GPC enabled third-party
The Global Privacy Control is an HTTP header that can be sent by a browser to instruct a visited website not to sell the user's personal data to other parties. This test checks to see if the GPC header is sent to third-party elements on the web page.
| passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false |
|
IP address leak
IP addresses can be used to uniquely identify a large percentage of users. A proxy, VPN, or Tor can mask a user's IP address.
| passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false, false, false, false |
passed: false, false |
|
Stream isolation
Browsers that use Tor can use a different Tor circuit per top-level website.
| write: () => {
if (!usingTor) {
throw new Error("Unsupported");
}
}
read: async () => {
if (usingTor) {
return ipAddress;
} else {
throw new Error("Unsupported");
}
}
unsupported: true, true, true, true, true
readSameFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
testFailed: false, false, false, false, false
|
write: () => {
if (!usingTor) {
throw new Error("Unsupported");
}
}
read: async () => {
if (usingTor) {
return ipAddress;
} else {
throw new Error("Unsupported");
}
}
unsupported: true, true, true, true, true
readSameFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
testFailed: false, false, false, false, false
|
write: () => {
if (!usingTor) {
throw new Error("Unsupported");
}
}
read: async () => {
if (usingTor) {
return ipAddress;
} else {
throw new Error("Unsupported");
}
}
unsupported: true, true, true, true, true
readSameFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
testFailed: false, false, false, false, false
|
write: () => {
if (!usingTor) {
throw new Error("Unsupported");
}
}
read: async () => {
if (usingTor) {
return ipAddress;
} else {
throw new Error("Unsupported");
}
}
unsupported: true, true, true, true, true
readSameFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
testFailed: false, false, false, false, false
|
write: () => {
if (!usingTor) {
throw new Error("Unsupported");
}
}
read: async () => {
if (usingTor) {
return ipAddress;
} else {
throw new Error("Unsupported");
}
}
unsupported: true, true, true, true, true
readSameFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
testFailed: false, false, false, false, false
|
write: () => {
if (!usingTor) {
throw new Error("Unsupported");
}
}
read: async () => {
if (usingTor) {
return ipAddress;
} else {
throw new Error("Unsupported");
}
}
unsupported: true, true, true, true, true
readSameFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
testFailed: false, false, false, false, false
|
write: () => {
if (!usingTor) {
throw new Error("Unsupported");
}
}
read: async () => {
if (usingTor) {
return ipAddress;
} else {
throw new Error("Unsupported");
}
}
unsupported: true, true, true, true, true
readSameFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
testFailed: false, false, false, false, false
|
write: () => {
if (!usingTor) {
throw new Error("Unsupported");
}
}
read: async () => {
if (usingTor) {
return ipAddress;
} else {
throw new Error("Unsupported");
}
}
unsupported: true, true, true, true, true
readSameFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
testFailed: false, false, false, false, false
|
write: () => {
if (!usingTor) {
throw new Error("Unsupported");
}
}
read: async () => {
if (usingTor) {
return ipAddress;
} else {
throw new Error("Unsupported");
}
}
unsupported: true, true, true, true, true
readSameFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
testFailed: false, false, false, false, false
|
write: () => {
if (!usingTor) {
throw new Error("Unsupported");
}
}
read: async () => {
if (usingTor) {
return ipAddress;
} else {
throw new Error("Unsupported");
}
}
unsupported: true, true
readSameFirstParty:
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty:
Error: Unsupported,
Error: Unsupported
testFailed: false, false
|
|
Tor enabled
The Tor network sends the browser's web requests through a series of relays to hide a user's IP address, thereby helping to mask their identity and location. This test checks to see if the Tor network is being used by default.
| IsTorExit: false, false, false, false, false passed: false, false, false, false, false |
IsTorExit: false, false, false, false, false passed: false, false, false, false, false |
IsTorExit: false, false, false, false, false passed: false, false, false, false, false |
IsTorExit: false, false, false, false, false passed: false, false, false, false, false |
IsTorExit: false, false, false, false, false passed: false, false, false, false, false |
IsTorExit: false, false, false, false, false passed: false, false, false, false, false |
IsTorExit: false, false, false, false, false passed: false, false, false, false, false |
IsTorExit: false, false, false, false, false passed: false, false, false, false, false |
IsTorExit: false, false, false, false, false passed: false, false, false, false, false |
IsTorExit: false, false passed: false, false |
|
Fingerprinting resistance testsWhich browsers hide what's unique about your device?
Fingerprinting is a technique trackers use to uniquely identify you as you browse the web. A fingerprinting script will measure several characteristics of your browser and, combining this data, will build a fingerprint that may uniquely identify you among web users. Browsers can introduce countermeasures, such as minimizing the distinguishing information disclosed by certain web APIs so your browser is harder to pick out from the crowd (so-called 'fingerprinting resistance').`,
| ||||||||||
|
Media query screen height
Height of the user's screen in pixels.
| expression: undefined desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 667,667 desired value: undefined passed: false,false |
|
Media query screen width
Width of the user's screen in pixels.
| expression: undefined desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: undefined desired expression: undefined actual value: 375,375 desired value: undefined passed: false,false |
|
outerHeight
Height of the browser window in pixels, including browser chrome.
| expression: outerHeight desired expression: undefined actual value: 543,543,543,543,543 desired value: undefined passed: false,false,false,false,false |
expression: outerHeight desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: outerHeight desired expression: undefined actual value: 546,546,546,546,546 desired value: undefined passed: false,false,false,false,false |
expression: outerHeight desired expression: undefined actual value: 561,561,561,561,561 desired value: undefined passed: false,false,false,false,false |
expression: outerHeight desired expression: undefined actual value: 526,526,526,526,526 desired value: undefined passed: false,false,false,false,false |
expression: outerHeight desired expression: undefined actual value: 547,547,547,547,547 desired value: undefined passed: false,false,false,false,false |
expression: outerHeight desired expression: undefined actual value: 555,555,555,555,555 desired value: undefined passed: false,false,false,false,false |
expression: outerHeight desired expression: undefined actual value: 1428,1428,1428,1428,1428 desired value: undefined passed: true,true,true,true,true |
expression: outerHeight desired expression: undefined actual value: 508,508,508,508,508 desired value: undefined passed: false,false,false,false,false |
expression: outerHeight desired expression: undefined actual value: 667,667 desired value: undefined passed: false,false |
|
screen.height
Height of the user's screen, in pixels.
| expression: screen.height desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: screen.height desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: screen.height desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: screen.height desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: screen.height desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: screen.height desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: screen.height desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: screen.height desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: screen.height desired expression: undefined actual value: 667,667,667,667,667 desired value: undefined passed: false,false,false,false,false |
expression: screen.height desired expression: undefined actual value: 667,667 desired value: undefined passed: false,false |
|
screen.width
Width of the user's screen, in pixels.
| expression: screen.width desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: screen.width desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: screen.width desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: screen.width desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: screen.width desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: screen.width desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: screen.width desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: screen.width desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: screen.width desired expression: undefined actual value: 375,375,375,375,375 desired value: undefined passed: false,false,false,false,false |
expression: screen.width desired expression: undefined actual value: 375,375 desired value: undefined passed: false,false |
|
screenX
Position, in pixels, of the left edge of the browser window on screen.
| expression: screenX desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenX desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenX desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenX desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenX desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenX desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenX desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenX desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenX desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenX desired expression: undefined actual value: 0,0 desired value: undefined passed: true,true |
|
screenY
Position, in pixels, of the top edge of the browser window on screen.
| expression: screenY desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenY desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenY desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenY desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenY desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenY desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenY desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenY desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenY desired expression: undefined actual value: 0,0,0,0,0 desired value: undefined passed: true,true,true,true,true |
expression: screenY desired expression: undefined actual value: 0,0 desired value: undefined passed: true,true |
|
Tracking query parameter testsWhich browsers remove URL parameters that can track you?
When you browse from one web page to another, tracking companies will frequently attach a 'tracking query parameter' to the address of the second web page. That query parameter may contain a unique identifier that tracks you individually as you browse the web. And these query parameters are frequently synchronized with cookies, making them a powerful tracking vector. Web browsers can protect you from known tracking query parameters by stripping them from web addresses before your browser sends them. (The set of tracking query parameters tested here was largely borrowed from Brave.)`
| ||||||||||
|
__hsfp
HubSpot tracking parameter
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
__hssc
HubSpot tracking parameter
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
__hstc
HubSpot tracking parameter
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
__s
Drip.com email address tracking parameter
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
_hsenc
HubSpot tracking parameter
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
_openstat
Yandex tracking parameter
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
dclid
DoubleClick Click ID (Google)
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
fbclid
Facebook Click Identifier
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
gclid
Google Click Identifier
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
hsCtaTracking
HubSpot tracking parameter
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
mc_eid
Mailchimp Email ID (email recipient's address)
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
mkt_tok
Adobe Marketo tracking parameter
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
ml_subscriber
MailerLite email tracking
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
ml_subscriber_hash
MailerLite email tracking
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
msclkid
Microsoft Click ID
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
oly_anon_id
Omeda marketing 'anonymous' customer id
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
oly_enc_id
Omeda marketing 'known' customer id
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
rb_clickid
Unknown high-entropy tracking parameter
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
s_cid
Adobe Site Catalyst tracking parameter
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
vero_conv
Vero tracking parameter
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
vero_id
Vero tracking parameter
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
wickedid
Wicked Reports e-commerce tracking
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
yclid
Yandex Click ID
| passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false, false, false, false |
passed: true, true, true, true, true |
value: 01752447911282684 passed: false, false, false, false, false |
value: 01752447911282684 passed: false, false |
|
Tracker content blocking testsWhich browsers block important known tracking scripts and pixels?
When you visit a web page, it frequently has third-party embedded tracking content, such as scripts and tracking pixels. These embedded components spy on you. Some browsers and browser extensions maintain list of tracking companies and block their content from being loaded. This section checks to see if a browser blocks 20 of the largest trackers listed by https://whotracks.me.`
| ||||||||||
|
Adobe
Tests whether the browser blocks the page from loading the tracker at https://munchkin.marketo.net/munchkin.js
| url: https://munchkin.marketo.net/munchkin.js passed: true, true, true, true, true |
url: https://munchkin.marketo.net/munchkin.js passed: false, false, false, false, false |
url: https://munchkin.marketo.net/munchkin.js passed: true, true, true, true, true |
url: https://munchkin.marketo.net/munchkin.js passed: false, false, false, false, false |
url: https://munchkin.marketo.net/munchkin.js passed: false, false, false, false, false |
url: https://munchkin.marketo.net/munchkin.js passed: true, true, true, true, true |
url: https://munchkin.marketo.net/munchkin.js passed: false, false, false, false, false |
url: https://munchkin.marketo.net/munchkin.js passed: false, false, false, false, false |
url: https://munchkin.marketo.net/munchkin.js passed: false, false, false, false, false |
url: https://munchkin.marketo.net/munchkin.js passed: false, false |
|
Adobe Audience Manager
Tests whether the browser blocks the page from loading the tracker at https://dpm.demdex.net/ibs
| url: https://dpm.demdex.net/ibs passed: true, true, true, true, true |
url: https://dpm.demdex.net/ibs passed: false, false, false, false, false |
url: https://dpm.demdex.net/ibs passed: true, true, true, true, true |
url: https://dpm.demdex.net/ibs passed: false, false, false, false, false |
url: https://dpm.demdex.net/ibs passed: false, false, false, false, false |
url: https://dpm.demdex.net/ibs passed: true, true, true, true, true |
url: https://dpm.demdex.net/ibs passed: false, false, false, false, false |
url: https://dpm.demdex.net/ibs passed: false, false, false, false, false |
url: https://dpm.demdex.net/ibs passed: false, false, false, false, false |
url: https://dpm.demdex.net/ibs passed: false, false |
|
Amazon adsystem
Tests whether the browser blocks the page from loading the tracker at https://s.amazon-adsystem.com/dcm
| url: https://s.amazon-adsystem.com/dcm passed: true, true, true, true, true |
url: https://s.amazon-adsystem.com/dcm passed: false, false, false, false, false |
url: https://s.amazon-adsystem.com/dcm passed: true, true, true, true, true |
url: https://s.amazon-adsystem.com/dcm passed: false, false, false, false, false |
url: https://s.amazon-adsystem.com/dcm passed: false, false, false, false, false |
url: https://s.amazon-adsystem.com/dcm passed: true, true, true, true, true |
url: https://s.amazon-adsystem.com/dcm passed: true, true, true, true, true |
url: https://s.amazon-adsystem.com/dcm passed: false, false, false, false, false |
url: https://s.amazon-adsystem.com/dcm passed: false, false, false, false, false |
url: https://s.amazon-adsystem.com/dcm passed: false, false |
|
AppNexus
Tests whether the browser blocks the page from loading the tracker at https://ib.adnxs.com/px?id=178248&t=1
| url: https://ib.adnxs.com/px?id=178248&t=1 passed: true, true, true, true, true |
url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false, false, false, false |
url: https://ib.adnxs.com/px?id=178248&t=1 passed: true, true, true, true, true |
url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false, false, false, false |
url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false, false, false, false |
url: https://ib.adnxs.com/px?id=178248&t=1 passed: true, true, true, true, true |
url: https://ib.adnxs.com/px?id=178248&t=1 passed: true, true, true, true, true |
url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false, false, false, false |
url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false, false, false, false |
url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false |
|
Bing Ads
Tests whether the browser blocks the page from loading the tracker at https://bat.bing.com/bat.js
| url: https://bat.bing.com/bat.js passed: true, true, true, true, true |
url: https://bat.bing.com/bat.js passed: false, false, false, false, false |
url: https://bat.bing.com/bat.js passed: true, true, true, true, true |
url: https://bat.bing.com/bat.js passed: false, false, false, false, false |
url: https://bat.bing.com/bat.js passed: false, false, false, false, false |
url: https://bat.bing.com/bat.js passed: false, false, false, false, false |
url: https://bat.bing.com/bat.js passed: false, false, false, false, false |
url: https://bat.bing.com/bat.js passed: false, false, false, false, false |
url: https://bat.bing.com/bat.js passed: false, false, false, false, false |
url: https://bat.bing.com/bat.js passed: false, false |
|
Chartbeat
Tests whether the browser blocks the page from loading the tracker at https://static.chartbeat.com/js/chartbeat.js
| url: https://static.chartbeat.com/js/chartbeat.js passed: true, true, true, true, true |
url: https://static.chartbeat.com/js/chartbeat.js passed: false, false, false, false, false |
url: https://static.chartbeat.com/js/chartbeat.js passed: true, true, true, true, true |
url: https://static.chartbeat.com/js/chartbeat.js passed: false, false, false, false, false |
url: https://static.chartbeat.com/js/chartbeat.js passed: false, false, false, false, false |
url: https://static.chartbeat.com/js/chartbeat.js passed: true, true, true, true, true |
url: https://static.chartbeat.com/js/chartbeat.js passed: false, false, false, false, false |
url: https://static.chartbeat.com/js/chartbeat.js passed: true, true, true, true, true |
url: https://static.chartbeat.com/js/chartbeat.js passed: false, false, false, false, false |
url: https://static.chartbeat.com/js/chartbeat.js passed: false, false |
|
Criteo
Tests whether the browser blocks the page from loading the tracker at https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx
| url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: true, true, true, true, true |
url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false, false, false, false |
url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: true, true, true, true, true |
url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false, false, false, false |
url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false, false, false, false |
url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: true, true, true, true, true |
url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false, false, false, false |
url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false, false, false, false |
url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false, false, false, false |
url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false |
|
DoubleClick (Google)
Tests whether the browser blocks the page from loading the tracker at https://securepubads.g.doubleclick.net/static/glade.js
| url: https://securepubads.g.doubleclick.net/static/glade.js passed: true, true, true, true, true |
url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false, false, false, false |
url: https://securepubads.g.doubleclick.net/static/glade.js passed: true, true, true, true, true |
url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false, false, false, false |
url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false, false, false, false |
url: https://securepubads.g.doubleclick.net/static/glade.js passed: true, true, true, true, true |
url: https://securepubads.g.doubleclick.net/static/glade.js passed: true, true, true, true, true |
url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false, false, false, false |
url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false, false, false, false |
url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false |
|
Facebook tracking
Tests whether the browser blocks the page from loading the tracker at https://connect.facebook.net/en_US/fbevents.js
| url: https://connect.facebook.net/en_US/fbevents.js passed: true, true, true, true, true |
url: https://connect.facebook.net/en_US/fbevents.js passed: false, false, false, false, false |
url: https://connect.facebook.net/en_US/fbevents.js passed: true, true, true, true, true |
url: https://connect.facebook.net/en_US/fbevents.js passed: false, false, false, false, false |
url: https://connect.facebook.net/en_US/fbevents.js passed: false, false, false, false, false |
url: https://connect.facebook.net/en_US/fbevents.js passed: true, true, true, true, true |
url: https://connect.facebook.net/en_US/fbevents.js passed: false, false, false, false, false |
url: https://connect.facebook.net/en_US/fbevents.js passed: true, true, true, true, true |
url: https://connect.facebook.net/en_US/fbevents.js passed: false, false, false, false, false |
url: https://connect.facebook.net/en_US/fbevents.js passed: false, false |
|
Google (third-party ad pixel)
Tests whether the browser blocks the page from loading the tracker at https://www.google.com/pagead/1p-user-list/
| url: https://www.google.com/pagead/1p-user-list/ passed: true, true, true, true, true |
url: https://www.google.com/pagead/1p-user-list/ passed: false, false, false, false, false |
url: https://www.google.com/pagead/1p-user-list/ passed: true, true, true, true, true |
url: https://www.google.com/pagead/1p-user-list/ passed: false, false, false, false, false |
url: https://www.google.com/pagead/1p-user-list/ passed: false, false, false, false, false |
url: https://www.google.com/pagead/1p-user-list/ passed: false, false, false, false, false |
url: https://www.google.com/pagead/1p-user-list/ passed: true, true, true, true, true |
url: https://www.google.com/pagead/1p-user-list/ passed: false, false, false, false, false |
url: https://www.google.com/pagead/1p-user-list/ passed: false, false, false, false, false |
url: https://www.google.com/pagead/1p-user-list/ passed: false, false |
|
Google Analytics
Tests whether the browser blocks the page from loading the tracker at https://google-analytics.com/urchin.js
| url: https://google-analytics.com/urchin.js passed: true, true, true, true, true |
url: https://google-analytics.com/urchin.js passed: false, false, false, false, false |
url: https://google-analytics.com/urchin.js passed: true, true, true, true, true |
url: https://google-analytics.com/urchin.js passed: false, false, false, false, false |
url: https://google-analytics.com/urchin.js passed: false, false, false, false, false |
url: https://google-analytics.com/urchin.js passed: true, true, true, true, true |
url: https://google-analytics.com/urchin.js passed: false, false, false, false, false |
url: https://google-analytics.com/urchin.js passed: true, true, true, true, true |
url: https://google-analytics.com/urchin.js passed: false, false, false, false, false |
url: https://google-analytics.com/urchin.js passed: false, false |
|
Google Tag Manager
Tests whether the browser blocks the page from loading the tracker at https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL
| url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: true, true, true, true, true |
url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false, false, false, false |
url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: true, true, true, true, true |
url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false, false, false, false |
url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false, false, false, false |
url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false, false, false, false |
url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false, false, false, false |
url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: true, true, true, true, true |
url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false, false, false, false |
url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false |
|
Index Exchange
Tests whether the browser blocks the page from loading the tracker at https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1
| url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: true, true, true, true, true |
url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false, false, false, false |
url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: true, true, true, true, true |
url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false, false, false, false |
url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false, false, false, false |
url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: true, true, true, true, true |
url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: true, true, true, true, true |
url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: true, true, true, true, true |
url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false, false, false, false |
url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false |
|
New Relic
Tests whether the browser blocks the page from loading the tracker at https://js-agent.newrelic.com/nr-1212.min.js
| url: https://js-agent.newrelic.com/nr-1212.min.js passed: true, true, true, true, true |
url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false, false, false, false |
url: https://js-agent.newrelic.com/nr-1212.min.js passed: true, true, true, true, true |
url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false, false, false, false |
url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false, false, false, false |
url: https://js-agent.newrelic.com/nr-1212.min.js passed: true, true, true, true, true |
url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false, false, false, false |
url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false, false, false, false |
url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false, false, false, false |
url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false |
|
Quantcast
Tests whether the browser blocks the page from loading the tracker at https://pixel.quantserve.com/pixel
| url: https://pixel.quantserve.com/pixel passed: true, true, true, true, true |
url: https://pixel.quantserve.com/pixel passed: false, false, false, false, false |
url: https://pixel.quantserve.com/pixel passed: true, true, true, true, true |
url: https://pixel.quantserve.com/pixel passed: false, false, false, false, false |
url: https://pixel.quantserve.com/pixel passed: false, false, false, false, false |
url: https://pixel.quantserve.com/pixel passed: true, true, true, true, true |
url: https://pixel.quantserve.com/pixel passed: false, false, false, false, false |
url: https://pixel.quantserve.com/pixel passed: true, true, true, true, true |
url: https://pixel.quantserve.com/pixel passed: false, false, false, false, false |
url: https://pixel.quantserve.com/pixel passed: false, false |
|
Scorecard Research Beacon
Tests whether the browser blocks the page from loading the tracker at https://sb.scorecardresearch.com/internal-c2/default/cs.js
| url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: true, true, true, true, true |
url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false, false, false, false |
url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: true, true, true, true, true |
url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false, false, false, false |
url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false, false, false, false |
url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: true, true, true, true, true |
url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false, false, false, false |
url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false, false, false, false |
url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false, false, false, false |
url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false |
|
Taboola
Tests whether the browser blocks the page from loading the tracker at https://trc.taboola.com/futureplc-tomsguide/trc/3/json
| url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: true, true, true, true, true |
url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false, false, false, false |
url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: true, true, true, true, true |
url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false, false, false, false |
url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false, false, false, false |
url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: true, true, true, true, true |
url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false, false, false, false |
url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false, false, false, false |
url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false, false, false, false |
url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false |
|
Twitter pixel
Tests whether the browser blocks the page from loading the tracker at https://t.co/i/adsct
| url: https://t.co/i/adsct passed: true, true, true, true, true |
url: https://t.co/i/adsct passed: false, false, false, false, false |
url: https://t.co/i/adsct passed: true, true, true, true, true |
url: https://t.co/i/adsct passed: false, false, false, false, false |
url: https://t.co/i/adsct passed: false, false, false, false, false |
url: https://t.co/i/adsct passed: false, false, false, false, false |
url: https://t.co/i/adsct passed: false, false, false, false, false |
url: https://t.co/i/adsct passed: false, false, false, false, false |
url: https://t.co/i/adsct passed: false, false, false, false, false |
url: https://t.co/i/adsct passed: false, false |
|
Yandex Ads
Tests whether the browser blocks the page from loading the tracker at https://yandex.ru/ads/system/header-bidding.js
| url: https://yandex.ru/ads/system/header-bidding.js passed: true, true, true, true, true |
url: https://yandex.ru/ads/system/header-bidding.js passed: false, false, false, false, false |
url: https://yandex.ru/ads/system/header-bidding.js passed: true, true, true, true, true |
url: https://yandex.ru/ads/system/header-bidding.js passed: false, false, false, false, false |
url: https://yandex.ru/ads/system/header-bidding.js passed: false, false, false, false, false |
url: https://yandex.ru/ads/system/header-bidding.js passed: false, false, false, false, false |
url: https://yandex.ru/ads/system/header-bidding.js passed: true, true, true, true, true |
url: https://yandex.ru/ads/system/header-bidding.js passed: false, false, false, false, false |
url: https://yandex.ru/ads/system/header-bidding.js passed: false, false, false, false, false |
url: https://yandex.ru/ads/system/header-bidding.js passed: false, false |
|
Cross-session first-party tracking testsWhich browsers prevent websites from tracking you across browser sessions?
A common vulnerability of web browsers is that they allow websites ("first parties") to 'tag' your browser with some tracking data. This tag can be used to re-identify you when you return to a website you visited before. This category of leaks can be prevented by browser if they clean or isolate data between browser sessions. (In cases where a user has logged into a website or entered detailed information, it may be justifiable for a browser to retain information across sessions. These tests check when no such justification exists: when you have entered no significant information into a website, will the browser still retain data that allows you to be tracked across sessions?)
| ||||||||||
|
Alt-Svc
Alt-Svc allows the server to indicate to the web browser that a resource should be loaded on a different server. Because this is a persistent setting, it could be used to track users across websites if it is not correctly partitioned.
| write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h3, h2, h3, h2
result, different session: h3, h2, h3, h2, h3
unsupported: true, false, true, false, true
passed: , true, true
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h3, h2, h3, h2
result, different session: h3, h2, h3, h2, h3
unsupported: true, false, true, false, true
passed: , true, true
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h3, h2, h3, h2
result, different session: h3, h2, h3, h2, h3
unsupported: true, false, true, false, true
passed: , true, true
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h3, h2, h3, h2
result, different session: h3, h2, h3, h2, h3
unsupported: true, false, true, false, true
passed: , true, true
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h3, h2, h3, h2
result, different session: h3, h2, h3, h2, h3
unsupported: true, false, true, false, true
passed: , true, true
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2, h2, h2, h2
result, different session: h2, h2, h2, h2, h2
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2, h2, h3, h2
result, different session: h3, h2, h3, h2, h3
unsupported: true, true, true, false, true
passed: , true
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2, h2, h2, h2
result, different session: h2, h2, h2, h2, h2
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h3, h2, h3, h2
result, different session: h3, h2, h3, h2, h3
unsupported: true, false, true, false, true
passed: , true, true
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2
result, different session: h2, h2
unsupported: true, true
passed: undefined
test failed: false, false
|
|
CacheStorage
The Cache API is a content storage mechanism originally introduced to support ServiceWorkers. If the same Cache object is accessible to multiple websites, it can be abused to track users.
| write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
2867a272-3459-465c-88f5-c8c0601a7e8c_1p,
a017c498-69a8-4e1c-bde8-188025a5fc0e_1p,
843217a8-9a9f-4727-84fe-c9023de914c6_1p,
c223fe48-a77f-4087-a844-72878ae074ce_1p,
2f51ceb9-93b5-4f31-8fc6-26429268e18b_1p
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
3381129f-ab1e-4185-897a-9961f05c5a89_1p,
8a73aa6a-249f-456f-aa36-7d299a7f4bab_1p,
aea7d626-1f84-4d5f-aa5f-f481621f7a77_1p,
ec31548d-380e-405b-8425-e1f9a359fe1b_1p,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234_1p
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_1p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_1p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_1p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_1p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_1p
result, different session:
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_1p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_1p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_1p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_1p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_1p
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f_1p,
d648fea1-92c9-4677-98bc-37477e3a6935_1p,
7c2ccb5a-8757-43b0-809c-92693c5728a8_1p,
d6429a17-238b-4b71-8e41-ab16a05fd3e2_1p,
8a22edfa-bdde-4576-aae0-5a5b2459fe17_1p
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
81be7770-de26-46ba-a344-6855e2651f4e_1p,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51_1p,
b1fae351-a866-4214-a922-24e8eba8325e_1p,
f27fcb7b-0656-432b-a99c-29f0069fc109_1p,
52835348-3201-47b2-8505-8c648dbd0432_1p
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
89fb71a3-e670-4f00-a55d-b90e9aed4261_1p,
2938ddfd-f5a0-4245-b96d-e8c629a66498_1p,
a8cbb18e-e019-4292-80b6-78528b4dae06_1p,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24_1p,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe_1p
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55_1p,
f3bd2113-34b8-4cc3-802e-76d69472d962_1p,
c240f51b-e239-4982-8f53-824fc630fb9f_1p,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e_1p,
64712c28-73a1-4e49-b42b-2c37152d98cb_1p
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_1p,
8a2376aa-acdf-4f53-99e8-517a83b724eb_1p
result, different session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_1p,
8a2376aa-acdf-4f53-99e8-517a83b724eb_3p
unsupported: false, false
passed: false, true
test failed: false, false
|
|
cookie (HTTP)
The cookie, first introduced by Netscape in 1994, is a small amount of data stored by your browser on a website's behalf. It has legitimate uses, but it is also the classic cross-site tracking mechanism, and today still the most popular method of tracking users across websites. Browsers can stop cookies from being used for cross-site tracking by either blocking or partitioning them.
| write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session:
2867a272-3459-465c-88f5-c8c0601a7e8c_1p_http,
a017c498-69a8-4e1c-bde8-188025a5fc0e_1p_http,
843217a8-9a9f-4727-84fe-c9023de914c6_1p_http,
c223fe48-a77f-4087-a844-72878ae074ce_1p_http,
2f51ceb9-93b5-4f31-8fc6-26429268e18b_1p_http
result, different session:
2867a272-3459-465c-88f5-c8c0601a7e8c_1p_http,
a017c498-69a8-4e1c-bde8-188025a5fc0e_1p_http,
843217a8-9a9f-4727-84fe-c9023de914c6_1p_http,
c223fe48-a77f-4087-a844-72878ae074ce_1p_http,
2f51ceb9-93b5-4f31-8fc6-26429268e18b_1p_http
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session:
3381129f-ab1e-4185-897a-9961f05c5a89_1p_http,
8a73aa6a-249f-456f-aa36-7d299a7f4bab_1p_http,
aea7d626-1f84-4d5f-aa5f-f481621f7a77_1p_http,
ec31548d-380e-405b-8425-e1f9a359fe1b_1p_http,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234_1p_http
result, different session:
3381129f-ab1e-4185-897a-9961f05c5a89_1p_http,
8a73aa6a-249f-456f-aa36-7d299a7f4bab_1p_http,
aea7d626-1f84-4d5f-aa5f-f481621f7a77_1p_http,
ec31548d-380e-405b-8425-e1f9a359fe1b_1p_http,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234_1p_http
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session:
e3162a38-9dff-4f7a-8c43-a42c5e945177_1p_http,
d940278d-5bd6-490e-9de4-a5ce7c6e3346_1p_http,
93972f1c-4b55-48e7-b36c-89c55f81499d_1p_http,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3_1p_http,
052bd326-3e1e-457e-a2a1-4de5bceb1764_1p_http
result, different session:
e3162a38-9dff-4f7a-8c43-a42c5e945177_1p_http,
d940278d-5bd6-490e-9de4-a5ce7c6e3346_1p_http,
93972f1c-4b55-48e7-b36c-89c55f81499d_1p_http,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3_1p_http,
052bd326-3e1e-457e-a2a1-4de5bceb1764_1p_http
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f_1p_http,
d648fea1-92c9-4677-98bc-37477e3a6935_1p_http,
7c2ccb5a-8757-43b0-809c-92693c5728a8_1p_http,
d6429a17-238b-4b71-8e41-ab16a05fd3e2_1p_http,
8a22edfa-bdde-4576-aae0-5a5b2459fe17_1p_http
result, different session:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f_1p_http,
d648fea1-92c9-4677-98bc-37477e3a6935_1p_http,
7c2ccb5a-8757-43b0-809c-92693c5728a8_1p_http,
d6429a17-238b-4b71-8e41-ab16a05fd3e2_1p_http,
8a22edfa-bdde-4576-aae0-5a5b2459fe17_1p_http
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session:
81be7770-de26-46ba-a344-6855e2651f4e_1p_http,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51_1p_http,
b1fae351-a866-4214-a922-24e8eba8325e_1p_http,
f27fcb7b-0656-432b-a99c-29f0069fc109_1p_http,
52835348-3201-47b2-8505-8c648dbd0432_1p_http
result, different session:
81be7770-de26-46ba-a344-6855e2651f4e_1p_http,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51_1p_http,
b1fae351-a866-4214-a922-24e8eba8325e_1p_http,
f27fcb7b-0656-432b-a99c-29f0069fc109_1p_http,
52835348-3201-47b2-8505-8c648dbd0432_1p_http
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session:
df336d08-f790-4b43-8c0d-f035748ce923_1p_http,
71e9a742-0190-4ced-b3ae-474d3c125c49_1p_http,
11bbfc32-cceb-4e06-96f1-f616876b2aa7_1p_http,
f0b86440-e911-449e-b536-1cee79072fc3_1p_http,
afb7ec45-db89-49ab-aacf-f3765763d32d_1p_http
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session:
89fb71a3-e670-4f00-a55d-b90e9aed4261_1p_http,
2938ddfd-f5a0-4245-b96d-e8c629a66498_1p_http,
a8cbb18e-e019-4292-80b6-78528b4dae06_1p_http,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24_1p_http,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe_1p_http
result, different session:
89fb71a3-e670-4f00-a55d-b90e9aed4261_1p_http,
2938ddfd-f5a0-4245-b96d-e8c629a66498_1p_http,
a8cbb18e-e019-4292-80b6-78528b4dae06_1p_http,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24_1p_http,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe_1p_http
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session:
45f08187-1804-4edc-8ea5-864c7aca7e23_1p_http,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc_1p_http,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc_1p_http,
592b7313-24d0-455a-b9c4-2bbafee9d271_1p_http,
5d215dd1-0fc4-4f5e-a510-b649d179ba24_1p_http
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55_1p_http,
f3bd2113-34b8-4cc3-802e-76d69472d962_1p_http,
c240f51b-e239-4982-8f53-824fc630fb9f_1p_http,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e_1p_http,
64712c28-73a1-4e49-b42b-2c37152d98cb_1p_http
result, different session:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55_1p_http,
f3bd2113-34b8-4cc3-802e-76d69472d962_1p_http,
c240f51b-e239-4982-8f53-824fc630fb9f_1p_http,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e_1p_http,
64712c28-73a1-4e49-b42b-2c37152d98cb_1p_http
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_1p_http,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5_1p_http
result, different session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_1p_http,
unsupported: false, false
passed: false, true
test failed: false, false
|
|
cookie (JS)
The cookie, first introduced by Netscape in 1994, is a small amount of data stored by your browser on a website's behalf. It has legitimate uses, but it is also the classic cross-site tracking mechanism, and today still the most popular method of tracking users across websites. Browsers can stop cookies from being used for cross-site tracking by either blocking or partitioning them.
| write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session:
2867a272-3459-465c-88f5-c8c0601a7e8c_1p_js,
a017c498-69a8-4e1c-bde8-188025a5fc0e_1p_js,
843217a8-9a9f-4727-84fe-c9023de914c6_1p_js,
c223fe48-a77f-4087-a844-72878ae074ce_1p_js,
2f51ceb9-93b5-4f31-8fc6-26429268e18b_1p_js
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session:
3381129f-ab1e-4185-897a-9961f05c5a89_1p_js,
8a73aa6a-249f-456f-aa36-7d299a7f4bab_1p_js,
aea7d626-1f84-4d5f-aa5f-f481621f7a77_1p_js,
ec31548d-380e-405b-8425-e1f9a359fe1b_1p_js,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234_1p_js
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session:
e3162a38-9dff-4f7a-8c43-a42c5e945177_1p_js,
d940278d-5bd6-490e-9de4-a5ce7c6e3346_1p_js,
93972f1c-4b55-48e7-b36c-89c55f81499d_1p_js,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3_1p_js,
052bd326-3e1e-457e-a2a1-4de5bceb1764_1p_js
result, different session:
e3162a38-9dff-4f7a-8c43-a42c5e945177_1p_js,
d940278d-5bd6-490e-9de4-a5ce7c6e3346_1p_js,
93972f1c-4b55-48e7-b36c-89c55f81499d_1p_js,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3_1p_js,
052bd326-3e1e-457e-a2a1-4de5bceb1764_1p_js
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f_1p_js,
d648fea1-92c9-4677-98bc-37477e3a6935_1p_js,
7c2ccb5a-8757-43b0-809c-92693c5728a8_1p_js,
d6429a17-238b-4b71-8e41-ab16a05fd3e2_1p_js,
8a22edfa-bdde-4576-aae0-5a5b2459fe17_1p_js
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session:
81be7770-de26-46ba-a344-6855e2651f4e_1p_js,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51_1p_js,
b1fae351-a866-4214-a922-24e8eba8325e_1p_js,
f27fcb7b-0656-432b-a99c-29f0069fc109_1p_js,
52835348-3201-47b2-8505-8c648dbd0432_1p_js
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session:
df336d08-f790-4b43-8c0d-f035748ce923_1p_js,
71e9a742-0190-4ced-b3ae-474d3c125c49_1p_js,
11bbfc32-cceb-4e06-96f1-f616876b2aa7_1p_js,
f0b86440-e911-449e-b536-1cee79072fc3_1p_js,
afb7ec45-db89-49ab-aacf-f3765763d32d_1p_js
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session:
89fb71a3-e670-4f00-a55d-b90e9aed4261_1p_js,
2938ddfd-f5a0-4245-b96d-e8c629a66498_1p_js,
a8cbb18e-e019-4292-80b6-78528b4dae06_1p_js,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24_1p_js,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe_1p_js
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session:
45f08187-1804-4edc-8ea5-864c7aca7e23_1p_js,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc_1p_js,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc_1p_js,
592b7313-24d0-455a-b9c4-2bbafee9d271_1p_js,
5d215dd1-0fc4-4f5e-a510-b649d179ba24_1p_js
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55_1p_js,
f3bd2113-34b8-4cc3-802e-76d69472d962_1p_js,
c240f51b-e239-4982-8f53-824fc630fb9f_1p_js,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e_1p_js,
64712c28-73a1-4e49-b42b-2c37152d98cb_1p_js
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_1p_js,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5_1p_js
result, different session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_1p_js,
unsupported: false, false
passed: false, true
test failed: false, false
|
|
CookieStore
The Cookie Store API is an alternative asynchronous API for managing cookies, supported by some browsers.
| write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported
unsupported: true, true
passed: undefined
test failed: false, false
|
|
CSS cache
CSS stylesheets are cached, and if that cache is shared between websites, it can be used to track users across sites.
| write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_06592442406892363,
fake_008359909211135497,
fake_9710130268731414,
fake_7473799270229955,
fake_5676176528914638
result, different session:
fake_06592442406892363,
fake_008359909211135497,
fake_9710130268731414,
fake_7473799270229955,
fake_5676176528914638
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_4316534385527866,
fake_6174706104335044,
fake_8001723439267017,
fake_5119734111018923,
fake_9870532484899257
result, different session:
fake_4316534385527866,
fake_6174706104335044,
fake_8001723439267017,
fake_5119734111018923,
fake_9870532484899257
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_8796902673324791,
fake_025713625607598267,
fake_8973344185403402,
fake_7517295947799769,
fake_4680823324577861
result, different session:
fake_8796902673324791,
fake_025713625607598267,
fake_8973344185403402,
fake_7517295947799769,
fake_4680823324577861
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_8698102554281404,
fake_33967649431731606,
fake_26349335361762716,
fake_8314502166978504,
fake_5890928296600013
result, different session:
fake_8698102554281404,
fake_33967649431731606,
fake_26349335361762716,
fake_8314502166978504,
fake_5890928296600013
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_1819327402356372,
fake_21362561122719526,
fake_9455960851213037,
fake_6737142498662911,
fake_7156123945013435
result, different session:
fake_1819327402356372,
fake_21362561122719526,
fake_9455960851213037,
fake_6737142498662911,
fake_7156123945013435
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_35356604352396026,
fake_6329782737149747,
fake_288824805996611,
fake_5039206334539954,
fake_07845499008060397
result, different session:
fake_7994676959127083,
fake_9628033870109338,
fake_6123124408275671,
fake_1122408079091275,
fake_8336029518422947
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_39330468556793763,
fake_5086050878199164,
fake_7999499682316082,
fake_18290470675572257,
fake_7057431606703419
result, different session:
fake_39330468556793763,
fake_5086050878199164,
fake_7999499682316082,
fake_18290470675572257,
fake_7057431606703419
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_3862098434873147,
fake_7744305319991567,
fake_43240510549577693,
fake_3906988195038561,
fake_6351543217089475
result, different session:
fake_5537376893079131,
fake_9405230172450967,
fake_5748201386777005,
fake_5574055095818977,
fake_482452696337343
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_10711939626814138,
fake_7975738080553825,
fake_02984518432482397,
fake_9303080216399637,
fake_18947172505114152
result, different session:
fake_10711939626814138,
fake_7975738080553825,
fake_02984518432482397,
fake_9303080216399637,
fake_18947172505114152
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_1752411502186244,
fake_2343789350497687
result, different session:
fake_06563245231964943,
fake_12331846713947003
unsupported: false, false
passed: true, true
test failed: false, false
|
|
favicon cache
A favicon is an icon that represents a website, typically shown in browser tab and bookmarks menu. If the favicon cache is not partitioned, it can be used to track users across websites.
| write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 2, 1, 2, 2, 1
result, different session: 2, 1, 2, 2, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false
|
|
fetch cache
When a resource is received via the Fetch API, it is frequently cached. That cache can potentially be abused for cross-site tracking.
| write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 2, 2, 2, 2, 2
result, different session: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 2, 2, 2, 2, 2
result, different session: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1
result, different session: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|
|
font cache
Web fonts are sometimes stored in their own cache, which is vulnerable to being abused for cross-site tracking.
| write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1
result, different session: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|
|
iframe cache
An iframe is an element in a web page than allows websites to embed a second web page. Caching of this web page could be abused for cross-site tracking.
| write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1
result, different session: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|
|
image cache
Caching of images in web browsers is a standard behavior. But if that cache leaks between websites, it can be abused for cross-site tracking.
| write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1
result, different session: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|
|
indexedDB
The IndexedDB API exposes a transactional database to web pages. That database can be used to track users across websites, unless it is partitioned.
| write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
2867a272-3459-465c-88f5-c8c0601a7e8c_1p,
a017c498-69a8-4e1c-bde8-188025a5fc0e_1p,
843217a8-9a9f-4727-84fe-c9023de914c6_1p,
c223fe48-a77f-4087-a844-72878ae074ce_1p,
2f51ceb9-93b5-4f31-8fc6-26429268e18b_1p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
3381129f-ab1e-4185-897a-9961f05c5a89_1p,
8a73aa6a-249f-456f-aa36-7d299a7f4bab_1p,
aea7d626-1f84-4d5f-aa5f-f481621f7a77_1p,
ec31548d-380e-405b-8425-e1f9a359fe1b_1p,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234_1p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
e3162a38-9dff-4f7a-8c43-a42c5e945177_1p,
d940278d-5bd6-490e-9de4-a5ce7c6e3346_1p,
93972f1c-4b55-48e7-b36c-89c55f81499d_1p,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3_1p,
052bd326-3e1e-457e-a2a1-4de5bceb1764_1p
result, different session:
e3162a38-9dff-4f7a-8c43-a42c5e945177_1p,
d940278d-5bd6-490e-9de4-a5ce7c6e3346_1p,
93972f1c-4b55-48e7-b36c-89c55f81499d_1p,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3_1p,
052bd326-3e1e-457e-a2a1-4de5bceb1764_1p
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f_1p,
d648fea1-92c9-4677-98bc-37477e3a6935_1p,
7c2ccb5a-8757-43b0-809c-92693c5728a8_1p,
d6429a17-238b-4b71-8e41-ab16a05fd3e2_1p,
8a22edfa-bdde-4576-aae0-5a5b2459fe17_1p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
81be7770-de26-46ba-a344-6855e2651f4e_1p,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51_1p,
b1fae351-a866-4214-a922-24e8eba8325e_1p,
f27fcb7b-0656-432b-a99c-29f0069fc109_1p,
52835348-3201-47b2-8505-8c648dbd0432_1p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
df336d08-f790-4b43-8c0d-f035748ce923_1p,
71e9a742-0190-4ced-b3ae-474d3c125c49_1p,
11bbfc32-cceb-4e06-96f1-f616876b2aa7_1p,
f0b86440-e911-449e-b536-1cee79072fc3_1p,
afb7ec45-db89-49ab-aacf-f3765763d32d_1p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
89fb71a3-e670-4f00-a55d-b90e9aed4261_1p,
2938ddfd-f5a0-4245-b96d-e8c629a66498_1p,
a8cbb18e-e019-4292-80b6-78528b4dae06_1p,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24_1p,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe_1p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
45f08187-1804-4edc-8ea5-864c7aca7e23_1p,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc_1p,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc_1p,
592b7313-24d0-455a-b9c4-2bbafee9d271_1p,
5d215dd1-0fc4-4f5e-a510-b649d179ba24_1p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55_1p,
f3bd2113-34b8-4cc3-802e-76d69472d962_1p,
c240f51b-e239-4982-8f53-824fc630fb9f_1p,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e_1p,
64712c28-73a1-4e49-b42b-2c37152d98cb_1p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_1p,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5_1p
result, different session: 8a2376aa-acdf-4f53-99e8-517a83b724eb_1p
unsupported: false, false
passed: false, true
test failed: false, false
|
|
localStorage
The localStorage API gives websites access to a key-value database that will remain available across visits. If the localStorage API is not partitioned or blocked, it can also be used to track users across websites.
| write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
2867a272-3459-465c-88f5-c8c0601a7e8c_1p,
a017c498-69a8-4e1c-bde8-188025a5fc0e_1p,
843217a8-9a9f-4727-84fe-c9023de914c6_1p,
c223fe48-a77f-4087-a844-72878ae074ce_1p,
2f51ceb9-93b5-4f31-8fc6-26429268e18b_1p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
3381129f-ab1e-4185-897a-9961f05c5a89_1p,
8a73aa6a-249f-456f-aa36-7d299a7f4bab_1p,
aea7d626-1f84-4d5f-aa5f-f481621f7a77_1p,
ec31548d-380e-405b-8425-e1f9a359fe1b_1p,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234_1p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
e3162a38-9dff-4f7a-8c43-a42c5e945177_1p,
d940278d-5bd6-490e-9de4-a5ce7c6e3346_1p,
93972f1c-4b55-48e7-b36c-89c55f81499d_1p,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3_1p,
052bd326-3e1e-457e-a2a1-4de5bceb1764_1p
result, different session:
e3162a38-9dff-4f7a-8c43-a42c5e945177_1p,
d940278d-5bd6-490e-9de4-a5ce7c6e3346_1p,
93972f1c-4b55-48e7-b36c-89c55f81499d_1p,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3_1p,
052bd326-3e1e-457e-a2a1-4de5bceb1764_1p
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f_1p,
d648fea1-92c9-4677-98bc-37477e3a6935_1p,
7c2ccb5a-8757-43b0-809c-92693c5728a8_1p,
d6429a17-238b-4b71-8e41-ab16a05fd3e2_1p,
8a22edfa-bdde-4576-aae0-5a5b2459fe17_1p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
81be7770-de26-46ba-a344-6855e2651f4e_1p,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51_1p,
b1fae351-a866-4214-a922-24e8eba8325e_1p,
f27fcb7b-0656-432b-a99c-29f0069fc109_1p,
52835348-3201-47b2-8505-8c648dbd0432_1p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
df336d08-f790-4b43-8c0d-f035748ce923_1p,
71e9a742-0190-4ced-b3ae-474d3c125c49_1p,
11bbfc32-cceb-4e06-96f1-f616876b2aa7_1p,
f0b86440-e911-449e-b536-1cee79072fc3_1p,
afb7ec45-db89-49ab-aacf-f3765763d32d_1p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
89fb71a3-e670-4f00-a55d-b90e9aed4261_1p,
2938ddfd-f5a0-4245-b96d-e8c629a66498_1p,
a8cbb18e-e019-4292-80b6-78528b4dae06_1p,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24_1p,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe_1p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
45f08187-1804-4edc-8ea5-864c7aca7e23_1p,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc_1p,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc_1p,
592b7313-24d0-455a-b9c4-2bbafee9d271_1p,
5d215dd1-0fc4-4f5e-a510-b649d179ba24_1p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55_1p,
f3bd2113-34b8-4cc3-802e-76d69472d962_1p,
c240f51b-e239-4982-8f53-824fc630fb9f_1p,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e_1p,
64712c28-73a1-4e49-b42b-2c37152d98cb_1p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_1p,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5_1p
result, different session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_1p,
unsupported: false, false
passed: false, true
test failed: false, false
|
|
prefetch cache
A <link rel='prefetch'...> suggests to browsers they should fetch a resource ahead of time and cache it. But if browsers don't partition this cache, it can be used to track users across websites.
| write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received
unsupported: true, true
passed: undefined
test failed: false, false
|
|
script cache
Caching of scripts in web browsers is a standard behavior. But if that cache leaks between websites, it can be abused for cross-site tracking.
| write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1
result, different session: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|
|
XMLHttpRequest cache
Similar to the newer Fetch API, any resource received may be cached by the browser. The cache is potentially vulnerable to cross-site tracking attack.
| write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1
result, different session: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|
|
Cross-session third-party tracking testsWhich browsers prevent third-party trackers from tracking you across browser sessions?
A common vulnerability of web browsers is that they allow third-party trackers to 'tag' your browser with some tracking data. This tag can be used to re-identify you when you return to a website you visited before. This category of leaks can be prevented by browser if they clean or isolate data between browser sessions. (In cases where a user has logged into a website or entered detailed information, it may be justifiable for a browser to retain information across sessions. These tests check when no such justification exists: when you have entered no significant information into a website, will the browser still retain data that allows you to be tracked across sessions?)
| ||||||||||
|
Alt-Svc
Alt-Svc allows the server to indicate to the web browser that a resource should be loaded on a different server. Because this is a persistent setting, it could be used to track users across websites if it is not correctly partitioned.
| write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2, h2, h2, h2
result, different session: h3, h3, h3, h3, h3
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2, h2, h2, h2
result, different session: h3, h3, h3, h3, h3
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2, h2, h2, h2
result, different session: h3, h3, h3, h3, h3
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2, h2, h2, h2
result, different session: h3, h3, h3, h3, h3
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2, h2, h2, h2
result, different session: h3, h3, h3, h3, h3
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2, h2, h2, h2
result, different session: h2, h2, h2, h2, h2
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2, h2, h2, h2
result, different session: h3, h3, h3, h3, h3
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2, h2, h2, h2
result, different session: h2, h2, h2, h2, h2
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2, h2, h2, h2
result, different session: h3, h3, h3, h3, h3
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async () => {
// Clear Alt-Svc caching first.
let responseText = "";
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/clear");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after clear:", responseText);
// Store "h3" state in Alt-Svc cache
for (let i = 0; i < 3; ++i) {
await fetch(altSvcOrigin + "/set");
await sleepMs(100);
}
responseText = await fetchText(altSvcOrigin + "/protocol");
console.log("after set:", responseText);
}
read: async () => {
const protocol = await fetchText(altSvcOrigin + "/protocol");
if ((new URL(location)).searchParams.get("thirdparty") === "same") {
if (protocol !== "h3") {
throw new Error("Unsupported");
}
}
return protocol;
}
result, same session: h2, h2
result, different session: h2, h2
unsupported: true, true
passed: undefined
test failed: false, false
|
|
CacheStorage
The Cache API is a content storage mechanism originally introduced to support ServiceWorkers. If the same Cache object is accessible to multiple websites, it can be abused to track users.
| write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
2867a272-3459-465c-88f5-c8c0601a7e8c_3p,
a017c498-69a8-4e1c-bde8-188025a5fc0e_3p,
843217a8-9a9f-4727-84fe-c9023de914c6_3p,
c223fe48-a77f-4087-a844-72878ae074ce_3p,
2f51ceb9-93b5-4f31-8fc6-26429268e18b_3p
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
3381129f-ab1e-4185-897a-9961f05c5a89_3p,
8a73aa6a-249f-456f-aa36-7d299a7f4bab_3p,
aea7d626-1f84-4d5f-aa5f-f481621f7a77_3p,
ec31548d-380e-405b-8425-e1f9a359fe1b_3p,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234_3p
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_3p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_3p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_3p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_3p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_3p
result, different session:
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_3p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_3p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_3p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_3p,
c846cb44-12d2-41b3-b2a6-38ca1a4518ed_3p
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f_3p,
d648fea1-92c9-4677-98bc-37477e3a6935_3p,
7c2ccb5a-8757-43b0-809c-92693c5728a8_3p,
d6429a17-238b-4b71-8e41-ab16a05fd3e2_3p,
8a22edfa-bdde-4576-aae0-5a5b2459fe17_3p
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
81be7770-de26-46ba-a344-6855e2651f4e_3p,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51_3p,
b1fae351-a866-4214-a922-24e8eba8325e_3p,
f27fcb7b-0656-432b-a99c-29f0069fc109_3p,
52835348-3201-47b2-8505-8c648dbd0432_3p
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
89fb71a3-e670-4f00-a55d-b90e9aed4261_3p,
2938ddfd-f5a0-4245-b96d-e8c629a66498_3p,
a8cbb18e-e019-4292-80b6-78528b4dae06_3p,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24_3p,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe_3p
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55_3p,
f3bd2113-34b8-4cc3-802e-76d69472d962_3p,
c240f51b-e239-4982-8f53-824fc630fb9f_3p,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e_3p,
64712c28-73a1-4e49-b42b-2c37152d98cb_3p
result, different session:
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
try {
let cache = await caches.open("supercookies");
cache.addAll([`test.css?key=${key}`]);
} catch (e) {
throw new Error("Unsupported");
}
}
read: async () => {
let cache = await caches.open("supercookies");
let cacheKeys = await cache.keys();
let url = cacheKeys[0].url;
return (new URL(url)).searchParams.get("key");
}
result, same session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_3p,
8a2376aa-acdf-4f53-99e8-517a83b724eb_3p
result, different session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_3p,
8a2376aa-acdf-4f53-99e8-517a83b724eb_1p
unsupported: false, false
passed: false, true
test failed: false, false
|
|
cookie (HTTP)
The cookie, first introduced by Netscape in 1994, is a small amount of data stored by your browser on a website's behalf. It has legitimate uses, but it is also the classic cross-site tracking mechanism, and today still the most popular method of tracking users across websites. Browsers can stop cookies from being used for cross-site tracking by either blocking or partitioning them.
| write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (secret) => {
// Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
await fetch(`${baseURI}cookie?secret=${secret}_http`);
}
read: async () => {
// Test if we now send a requests with a 'cookie' header containing the secret.
let response = await fetch(`${baseURI}headers`);
let cookie = (await response.json())["cookie"];
return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
}
result, same session: ,
result, different session: , c279cb7e-5a12-4ef7-a175-6d79595fb2f5_1p_http
unsupported: true, true
passed: undefined
test failed: false, false
|
|
cookie (JS)
The cookie, first introduced by Netscape in 1994, is a small amount of data stored by your browser on a website's behalf. It has legitimate uses, but it is also the classic cross-site tracking mechanism, and today still the most popular method of tracking users across websites. Browsers can stop cookies from being used for cross-site tracking by either blocking or partitioning them.
| write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , , , ,
result, different session: , , , ,
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (secret) => {
document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
}
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: ,
result, different session: , c279cb7e-5a12-4ef7-a175-6d79595fb2f5_1p_js
unsupported: true, true
passed: undefined
test failed: false, false
|
|
CookieStore
The Cookie Store API is an alternative asynchronous API for managing cookies, supported by some browsers.
| write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (data) => {
const msPerHour = 60 * 60 * 1000;
if (!window.cookieStore) {
throw new Error("Unsupported");
}
window.cookieStore.set({
name: "partition_test",
value: data,
expires: Date.now() + msPerHour,
sameSite: "none"
});
}
read: async () => {
if (!window.cookieStore) {
throw new Error("Unsupported");
}
const cookie = await window.cookieStore.get("partition_test");
if (!cookie) {
return null;
}
return cookie.value;
}
result, same session:
Error: Unsupported,
Error: Unsupported
result, different session:
Error: Unsupported,
Error: Unsupported
unsupported: true, true
passed: undefined
test failed: false, false
|
|
CSS cache
CSS stylesheets are cached, and if that cache is shared between websites, it can be used to track users across sites.
| write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_27813313631991177,
fake_2450452328906756,
fake_1986569409160599,
fake_9854859434870888,
fake_19120052873586157
result, different session:
fake_27813313631991177,
fake_2450452328906756,
fake_1986569409160599,
fake_9854859434870888,
fake_19120052873586157
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_790307976591383,
fake_5417848542955073,
fake_000808921606819446,
fake_48083940220658006,
fake_6367783309507111
result, different session:
fake_790307976591383,
fake_5417848542955073,
fake_000808921606819446,
fake_48083940220658006,
fake_6367783309507111
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_39431489764289895,
fake_43380622633667154,
fake_9496874444305843,
fake_24852811452321322,
fake_8765101121969932
result, different session:
fake_39431489764289895,
fake_43380622633667154,
fake_9496874444305843,
fake_24852811452321322,
fake_8765101121969932
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_9218427664339655,
fake_012626772504535033,
fake_7914198190532769,
fake_09356456990957152,
fake_5020865873379081
result, different session:
fake_9218427664339655,
fake_012626772504535033,
fake_7914198190532769,
fake_09356456990957152,
fake_5020865873379081
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_27074651156317264,
fake_20968952669928864,
fake_1485127464083691,
fake_7338263942304655,
fake_3327093477589804
result, different session:
fake_27074651156317264,
fake_20968952669928864,
fake_1485127464083691,
fake_7338263942304655,
fake_3327093477589804
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_8496189787717465,
fake_3560396066938398,
fake_20087063043278608,
fake_8145872182308429,
fake_6952782366712107
result, different session:
fake_5399384541574752,
fake_3971046938230518,
fake_8714035328164143,
fake_7427478952680093,
fake_6462931635820406
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_02292064081505929,
fake_8330376179356727,
fake_5188493466510578,
fake_47016655683881137,
fake_9759740964057992
result, different session:
fake_02292064081505929,
fake_8330376179356727,
fake_5188493466510578,
fake_47016655683881137,
fake_9759740964057992
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_6970914904672478,
fake_10809024233907127,
fake_8173327532630639,
fake_2505598354696461,
fake_6760328815398964
result, different session:
fake_523666010922188,
fake_8696134106508839,
fake_17890412465629169,
fake_7327768354284045,
fake_33316628391072656
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_63497361680302,
fake_33552531507888284,
fake_19492699662310153,
fake_3198878061652848,
fake_2668752529116969
result, different session:
fake_63497361680302,
fake_33552531507888284,
fake_19492699662310153,
fake_3198878061652848,
fake_2668752529116969
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return key;
}
read: async (key) => {
const href = testURI("resource", "css", key);
const head = document.getElementsByTagName("head")[0];
head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
const testElement = document.querySelector("#css");
let fontFamily;
while (true) {
await sleepMs(100);
fontFamily = getComputedStyle(testElement).fontFamily;
if (fontFamily.startsWith("fake")) {
break;
}
}
console.log(fontFamily);
return fontFamily;
}
result, same session:
fake_8367287083476282,
fake_08994573291486918
result, different session:
fake_9507955086712121,
fake_39154127949397766
unsupported: false, false
passed: true, true
test failed: false, false
|
|
favicon cache
A favicon is an icon that represents a website, typically shown in browser tab and bookmarks menu. If the favicon cache is not partitioned, it can be used to track users across websites.
| write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, Error: No requests received, Error: No requests received, Error: No requests received, Error: No requests received
result, different session: 1, Error: No requests received, Error: No requests received, Error: No requests received, Error: No requests received
unsupported: false, true, true, true, true
passed: false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 2, 2, 2, 2, 2
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => key
read: async (key) => {
// Wait for the favicon to load (defined in supercookies.html)
await sleepMs(2000);
let response = await fetch(
testURI("ctr", "favicon", key), {"cache": "reload"});
let count = (await response.text()).trim();
if (count === "0") {
throw new Error("No requests received");
}
return count;
}
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false
|
|
fetch cache
When a resource is received via the Fetch API, it is frequently cached. That cache can potentially be abused for cross-site tracking.
| write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 2, 2, 2, 2, 2
result, different session: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 2, 2, 2, 2, 2
result, different session: 3, 3, 3, 3, 3
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
return key;
}
read: async (key) => {
let response = await fetch(testURI("resource", "fetch", key),
{cache: "force-cache"});
let countResponse = await fetch(testURI("ctr", "fetch", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1
result, different session: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|
|
font cache
Web fonts are sometimes stored in their own cache, which is vulnerable to being abused for cross-site tracking.
| write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
return key;
}
read: async (key) => {
const text = document.createElement("span");
text.id = "text";
text.innerText = "test";
document.body.appendChild(text);
const originalWidth = text.getBoundingClientRect().width;
let style = document.createElement("style");
style.type='text/css';
let fontURI = testURI("resource", "font", key);
style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
document.getElementsByTagName("head")[0].appendChild(style);
let newWidth;
do {
await sleepMs(100);
newWidth = text.getBoundingClientRect().width;
} while (newWidth < 0 || newWidth === originalWidth)
let response = await fetch(
testURI("ctr", "font", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1
result, different session: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|
|
iframe cache
An iframe is an element in a web page than allows websites to embed a second web page. Caching of this web page could be abused for cross-site tracking.
| write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => resolve(key), {once: true});
iframe.src = testURI("resource", "page", key);
})
read: async (key) => {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
let iframeLoadPromise = new Promise((resolve, reject) => {
iframe.addEventListener("load", resolve, {once: true});
});
let address = testURI("resource", "page", key);
iframe.src = address;
await iframeLoadPromise;
let response = await fetch(
testURI("ctr", "page", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1
result, different session: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|
|
image cache
Caching of images in web browsers is a standard behavior. But if that cache leaks between websites, it can be abused for cross-site tracking.
| write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let img = document.createElement("img");
document.body.appendChild(img);
img.addEventListener("load", () => resolve(key), {once: true});
img.src = testURI("resource", "image", key);
})
read: async (key) => {
let img = document.createElement("img");
document.body.appendChild(img);
let imgLoadPromise = new Promise((resolve, reject) => {
img.addEventListener("load", resolve, {once: true});
});
img.src = testURI("resource", "image", key);
await imgLoadPromise;
let response = await fetch(
testURI("ctr", "image", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1
result, different session: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|
|
indexedDB
The IndexedDB API exposes a transactional database to web pages. That database can be used to track users across websites, unless it is partitioned.
| write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
2867a272-3459-465c-88f5-c8c0601a7e8c_3p,
a017c498-69a8-4e1c-bde8-188025a5fc0e_3p,
843217a8-9a9f-4727-84fe-c9023de914c6_3p,
c223fe48-a77f-4087-a844-72878ae074ce_3p,
2f51ceb9-93b5-4f31-8fc6-26429268e18b_3p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
3381129f-ab1e-4185-897a-9961f05c5a89_3p,
8a73aa6a-249f-456f-aa36-7d299a7f4bab_3p,
aea7d626-1f84-4d5f-aa5f-f481621f7a77_3p,
ec31548d-380e-405b-8425-e1f9a359fe1b_3p,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234_3p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
e3162a38-9dff-4f7a-8c43-a42c5e945177_3p,
d940278d-5bd6-490e-9de4-a5ce7c6e3346_3p,
93972f1c-4b55-48e7-b36c-89c55f81499d_3p,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3_3p,
052bd326-3e1e-457e-a2a1-4de5bceb1764_3p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f_3p,
d648fea1-92c9-4677-98bc-37477e3a6935_3p,
7c2ccb5a-8757-43b0-809c-92693c5728a8_3p,
d6429a17-238b-4b71-8e41-ab16a05fd3e2_3p,
8a22edfa-bdde-4576-aae0-5a5b2459fe17_3p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
81be7770-de26-46ba-a344-6855e2651f4e_3p,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51_3p,
b1fae351-a866-4214-a922-24e8eba8325e_3p,
f27fcb7b-0656-432b-a99c-29f0069fc109_3p,
52835348-3201-47b2-8505-8c648dbd0432_3p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
df336d08-f790-4b43-8c0d-f035748ce923_3p,
71e9a742-0190-4ced-b3ae-474d3c125c49_3p,
11bbfc32-cceb-4e06-96f1-f616876b2aa7_3p,
f0b86440-e911-449e-b536-1cee79072fc3_3p,
afb7ec45-db89-49ab-aacf-f3765763d32d_3p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
89fb71a3-e670-4f00-a55d-b90e9aed4261_3p,
2938ddfd-f5a0-4245-b96d-e8c629a66498_3p,
a8cbb18e-e019-4292-80b6-78528b4dae06_3p,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24_3p,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe_3p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
45f08187-1804-4edc-8ea5-864c7aca7e23_3p,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc_3p,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc_3p,
592b7313-24d0-455a-b9c4-2bbafee9d271_3p,
5d215dd1-0fc4-4f5e-a510-b649d179ba24_3p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55_3p,
f3bd2113-34b8-4cc3-802e-76d69472d962_3p,
c240f51b-e239-4982-8f53-824fc630fb9f_3p,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e_3p,
64712c28-73a1-4e49-b42b-2c37152d98cb_3p
result, different session: undefined
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (secret) => {
try {
return await IdbKeyVal.set("secret", secret);
} catch (e) {
throw new Error("Unsupported");
}
}
read: () => IdbKeyVal.get("secret")
result, same session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_3p,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5_3p
result, different session: , c279cb7e-5a12-4ef7-a175-6d79595fb2f5_1p
unsupported: false, false
passed: true, true
test failed: false, false
|
|
localStorage
The localStorage API gives websites access to a key-value database that will remain available across visits. If the localStorage API is not partitioned or blocked, it can also be used to track users across websites.
| write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
2867a272-3459-465c-88f5-c8c0601a7e8c_3p,
a017c498-69a8-4e1c-bde8-188025a5fc0e_3p,
843217a8-9a9f-4727-84fe-c9023de914c6_3p,
c223fe48-a77f-4087-a844-72878ae074ce_3p,
2f51ceb9-93b5-4f31-8fc6-26429268e18b_3p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
3381129f-ab1e-4185-897a-9961f05c5a89_3p,
8a73aa6a-249f-456f-aa36-7d299a7f4bab_3p,
aea7d626-1f84-4d5f-aa5f-f481621f7a77_3p,
ec31548d-380e-405b-8425-e1f9a359fe1b_3p,
1b57d08d-9b09-4e8a-8cdd-b2aaa0840234_3p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
e3162a38-9dff-4f7a-8c43-a42c5e945177_3p,
d940278d-5bd6-490e-9de4-a5ce7c6e3346_3p,
93972f1c-4b55-48e7-b36c-89c55f81499d_3p,
aa9e5dc5-1cc7-410e-9b63-5186f464c3e3_3p,
052bd326-3e1e-457e-a2a1-4de5bceb1764_3p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
e62c4bb5-60f7-4a55-8f60-8e4a5158c69f_3p,
d648fea1-92c9-4677-98bc-37477e3a6935_3p,
7c2ccb5a-8757-43b0-809c-92693c5728a8_3p,
d6429a17-238b-4b71-8e41-ab16a05fd3e2_3p,
8a22edfa-bdde-4576-aae0-5a5b2459fe17_3p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
81be7770-de26-46ba-a344-6855e2651f4e_3p,
dcd9e4c9-1c9b-4533-8c44-08289a6a0d51_3p,
b1fae351-a866-4214-a922-24e8eba8325e_3p,
f27fcb7b-0656-432b-a99c-29f0069fc109_3p,
52835348-3201-47b2-8505-8c648dbd0432_3p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
df336d08-f790-4b43-8c0d-f035748ce923_3p,
71e9a742-0190-4ced-b3ae-474d3c125c49_3p,
11bbfc32-cceb-4e06-96f1-f616876b2aa7_3p,
f0b86440-e911-449e-b536-1cee79072fc3_3p,
afb7ec45-db89-49ab-aacf-f3765763d32d_3p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
89fb71a3-e670-4f00-a55d-b90e9aed4261_3p,
2938ddfd-f5a0-4245-b96d-e8c629a66498_3p,
a8cbb18e-e019-4292-80b6-78528b4dae06_3p,
f664c8aa-3054-4d72-a2c0-c1ff1f8fda24_3p,
22eafbfd-c5a4-4816-b3ef-a8f5011629fe_3p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
45f08187-1804-4edc-8ea5-864c7aca7e23_3p,
f081a313-3b2d-4a8b-ae32-7bd7784ea0cc_3p,
02603275-d817-4ad3-b03c-0aaf1c6f6bfc_3p,
592b7313-24d0-455a-b9c4-2bbafee9d271_3p,
5d215dd1-0fc4-4f5e-a510-b649d179ba24_3p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
509fb5fc-60a4-4d2f-b4f2-e90e9ae45a55_3p,
f3bd2113-34b8-4cc3-802e-76d69472d962_3p,
c240f51b-e239-4982-8f53-824fc630fb9f_3p,
3f271bd4-c40c-4c60-b4a8-6bba65e1123e_3p,
64712c28-73a1-4e49-b42b-2c37152d98cb_3p
result, different session: , , , ,
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session:
8a2376aa-acdf-4f53-99e8-517a83b724eb_3p,
c279cb7e-5a12-4ef7-a175-6d79595fb2f5_3p
result, different session: , c279cb7e-5a12-4ef7-a175-6d79595fb2f5_1p
unsupported: false, false
passed: true, true
test failed: false, false
|
|
prefetch cache
A <link rel='prefetch'...> suggests to browsers they should fetch a resource ahead of time and cache it. But if browsers don't partition this cache, it can be used to track users across websites.
| write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true, true, true
passed: undefined
test failed: false, false, false, false, false
|
write: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
return key;
}
read: async (key) => {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = testURI("resource", "prefetch", key);
document.getElementsByTagName("head")[0].appendChild(link);
await sleepMs(500);
let response = await fetch(
testURI("ctr", "prefetch", key), {"cache": "reload"});
let countString = (await response.text()).trim();
if (parseInt(countString) === 0) {
throw new Error("No requests received");
}
return countString;
}
result, same session:
Error: No requests received,
Error: No requests received
result, different session:
Error: No requests received,
Error: No requests received
unsupported: true, true
passed: undefined
test failed: false, false
|
|
script cache
Caching of scripts in web browsers is a standard behavior. But if that cache leaks between websites, it can be abused for cross-site tracking.
| write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: (key) => new Promise((resolve, reject) => {
let script = document.createElement("script");
document.body.appendChild(script);
script.addEventListener("load", () => resolve(key), {once: true});
script.src = testURI("resource", "script", key);
})
read: async (key) => {
let script = document.createElement("script");
document.body.appendChild(script);
let scriptLoadPromise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
});
script.src = testURI("resource", "script", key);
await scriptLoadPromise;
let response = await fetch(
testURI("ctr", "script", key), {"cache": "reload"});
return (await response.text()).trim();
}
result, same session: 1, 1
result, different session: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|
|
XMLHttpRequest cache
Similar to the newer Fetch API, any resource received may be cached by the browser. The cache is potentially vulnerable to cross-site tracking attack.
| write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 2, 2, 2, 2, 2
unsupported: false, false, false, false, false
passed: true, true, true, true, true
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1, 1, 1, 1
result, different session: 1, 1, 1, 1, 1
unsupported: false, false, false, false, false
passed: false, false, false, false, false
test failed: false, false, false, false, false
|
write: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
return key;
}
read: async (key) => {
const req = new XMLHttpRequest();
const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
req.open("GET", testURI("resource", "xhr", key));
req.send();
await loadPromise;
let countResponse = await fetch(testURI("ctr", "xhr", key),
{cache: "reload"});
return (await countResponse.text()).trim();
}
result, same session: 1, 1
result, different session: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false
|