diff --git a/TEMPLATES/complex/main.js b/TEMPLATES/complex/main.js
index 75cf67a..5bf769f 100644
--- a/TEMPLATES/complex/main.js
+++ b/TEMPLATES/complex/main.js
@@ -1,3 +1,5 @@
+const PAGE_NAME = 'name-of-the-page'; // FILL ME OUT!
+
const startButton = document.querySelector('#start');
const downloadButton = document.querySelector('#download');
@@ -22,7 +24,7 @@ const tests = [
// object that contains results of all tests
const results = {
- page: 'name-of-the-test', // FILL ME OUT!
+ page: `${PAGE_NAME}-test`,
date: null,
results: []
};
@@ -85,7 +87,7 @@ function runTests () {
updateSummary();
});
} else {
- valueSpan.innerHTML = resultToHTML(data);
+ valueSpan.innerHTML = resultToHTML(result);
resultObj.value = result || null;
}
} catch (e) {
@@ -106,7 +108,7 @@ function downloadTheResults () {
const a = document.createElement('a');
const url = window.URL.createObjectURL(new Blob([data], { type: 'application/json' }));
a.href = url;
- a.download = 'fingerprinting-results.json';
+ a.download = `${PAGE_NAME}-results.json`;
document.body.appendChild(a);
a.click();
diff --git a/features/harmful-apis/index.html b/features/harmful-apis/index.html
new file mode 100644
index 0000000..a217a1c
--- /dev/null
+++ b/features/harmful-apis/index.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+ Harmful APIs Test Page
+
+
+
+
+
+ [Home] β£ [Harmful APIs Test Page]
+
+ This page will test if web APIs that are considered harmful are available in your browser. Only availablity is verified, privacy concerns might have been mitigated.
+
+ Start test
+
+
+
+
+
+
+ API's for manual testing:
+
+ signed exchanges - click and look for 'application/signed-exchange' in the 'accept' header
+ direct/raw sockets - not shipped yet. Once shipped, only available in installed PWA's - look for availability of `TCPSocket` and `UDPSocket` classes.
+
+
+ Download the result
+
+
+
\ No newline at end of file
diff --git a/features/harmful-apis/main.js b/features/harmful-apis/main.js
new file mode 100644
index 0000000..7e304bd
--- /dev/null
+++ b/features/harmful-apis/main.js
@@ -0,0 +1,323 @@
+const PAGE_NAME = 'harmful-apis';
+
+const startButton = document.querySelector('#start');
+const downloadButton = document.querySelector('#download');
+
+const testsDiv = document.querySelector('#tests');
+const testsSummaryDiv = document.querySelector('#tests-summary');
+const testsDetailsDiv = document.querySelector('#tests-details');
+
+const tests = [
+ {
+ id: 'battery-api',
+ run: () => {
+ return ('getBattery' in navigator);
+ }
+ },
+ {
+ id: 'webbluetooth-api',
+ run: () => {
+ return ('bluetooth' in navigator);
+ }
+ },
+ {
+ id: 'webusb-api',
+ run: () => {
+ return ('usb' in navigator);
+ }
+ },
+ {
+ id: 'webnfc-api',
+ run: () => {
+ return ('NDEFReader' in window);
+ }
+ },
+ {
+ id: 'webserial-api',
+ run: () => {
+ return ('serial' in navigator);
+ }
+ },
+ {
+ id: 'webhid-api',
+ run: () => {
+ return ('hid' in navigator);
+ }
+ },
+ {
+ id: 'accelerometer-api',
+ run: () => {
+ return ('Accelerometer' in window);
+ }
+ },
+ {
+ id: 'linearaccelerationsensor-api',
+ run: () => {
+ return ('LinearAccelerationSensor' in window);
+ }
+ },
+ {
+ id: 'absoluteorientationsensor-api',
+ run: () => {
+ return ('AbsoluteOrientationSensor' in window);
+ }
+ },
+ {
+ id: 'gyroscope-api',
+ run: () => {
+ return ('Gyroscope' in window);
+ }
+ },
+ {
+ id: 'relativeorientationsensor-api',
+ run: () => {
+ return ('RelativeOrientationSensor' in window);
+ }
+ },
+ {
+ id: 'gravitysensor-api',
+ run: () => {
+ return ('GravitySensor' in window);
+ }
+ },
+ {
+ id: 'ambientlightsensor-api',
+ run: () => {
+ return ('AmbientLightSensor' in window);
+ }
+ },
+ {
+ id: 'magnetometer-api',
+ run: () => {
+ return ('Magnetometer' in window);
+ }
+ },
+ {
+ id: 'keyboard-api',
+ run: () => {
+ return ('keyboard' in navigator);
+ }
+ },
+ {
+ id: 'installed-related-apps-api',
+ run: () => {
+ return ('getInstalledRelatedApps' in navigator);
+ }
+ },
+ {
+ id: 'hardwareConcurrency-api',
+ run: () => {
+ return ('hardwareConcurrency' in navigator);
+ }
+ },
+ {
+ id: 'deviceMemory-api',
+ run: () => {
+ return ('deviceMemory' in navigator);
+ }
+ },
+ {
+ id: 'mediadevices-enumarete-devices-api',
+ run: () => {
+ return ('MediaDevices' in window) && ('enumerateDevices' in MediaDevices.prototype);
+ }
+ },
+ {
+ id: 'storage-manager-estimate-api',
+ run: () => {
+ return ('StorageManager' in window) && ('estimate' in StorageManager.prototype);
+ }
+ },
+ {
+ id: 'network-information-api',
+ run: () => {
+ return ('connection' in navigator);
+ }
+ },
+ {
+ id: 'client-hints-api',
+ run: () => {
+ return fetch('/reflect-headers')
+ .then(r => r.json())
+ .then(data => {
+ const chData = {
+ 'device-memory': data.headers['device-memory'],
+ downlink: data.headers.downlink,
+ dpr: data.headers.dpr,
+ ect: data.headers.ect,
+ rtt: data.headers.rtt,
+ 'viewport-width': data.headers['viewport-width']
+ };
+
+ if (Object.values(chData).every(i => i === undefined) && !('userAgentData' in navigator)) {
+ return false;
+ }
+
+ return true;
+ });
+ }
+ },
+ {
+ id: 'topics-api',
+ run: () => {
+ return ('browsingTopics' in document);
+ }
+ },
+ {
+ id: 'floc-api',
+ run: () => {
+ return ('interestCohort' in document);
+ }
+ },
+ {
+ id: 'idle-detection-api',
+ run: () => {
+ return ('IdleDetector' in window);
+ }
+ },
+ {
+ id: 'native-filesystem-api',
+ run: () => {
+ return ('showOpenFilePicker' in window);
+ }
+ },
+ {
+ id: 'background-sync-api',
+ run: () => {
+ return ('ServiceWorkerRegistration' in window) && ('sync' in ServiceWorkerRegistration.prototype);
+ }
+ },
+ {
+ id: 'periodic-sync-api',
+ run: () => {
+ return ('ServiceWorkerRegistration' in window) && ('periodicSync' in ServiceWorkerRegistration.prototype);
+ }
+ },
+ {
+ id: 'push-api',
+ run: () => {
+ return ('ServiceWorkerRegistration' in window) && ('pushManager' in ServiceWorkerRegistration.prototype);
+ }
+ },
+ {
+ id: 'first-party-sets-api',
+ run: () => {
+ if (!window.cookieStore) {
+ throw new Error('β Can\'t be tested - CookieStore not available');
+ }
+
+ return window.cookieStore.set({ name: 'first-party-sets-test', value: 'value' })
+ .then(() => window.cookieStore.getAll())
+ .then(cookies => {
+ const cookie = cookies.find(c => c.name === 'first-party-sets-test');
+
+ return ('sameParty' in cookie);
+ });
+ }
+ },
+ {
+ id: 'fledge',
+ run: () => {
+ return ('joinAdInterestGroup' in navigator) || ('runAdAuction' in navigator);
+ }
+ }
+];
+
+// object that contains results of all tests
+const results = {
+ page: `${PAGE_NAME}-test`,
+ date: null,
+ results: []
+};
+
+function resultToHTML (data) {
+ return ((data === false) ? 'API not available' : 'π API available');
+}
+
+/**
+ * Test runner
+ */
+function runTests () {
+ startButton.setAttribute('disabled', 'disabled');
+ downloadButton.removeAttribute('disabled');
+ testsDiv.removeAttribute('hidden');
+
+ results.results.length = 0;
+ results.date = (new Date()).toUTCString();
+ let all = 0;
+ let failed = 0;
+
+ testsDetailsDiv.innerHTML = '';
+
+ function updateSummary () {
+ testsSummaryDiv.innerText = `Performed ${all} tests${failed > 0 ? ` (${failed} failed)` : ''}. Click for details.`;
+ }
+
+ for (const test of tests) {
+ const resultObj = {
+ id: test.id,
+ value: null
+ };
+ results.results.push(resultObj);
+
+ const li = document.createElement('li');
+ li.id = `test-${test.id.replace(' ', '-')}`;
+ li.innerHTML = `${test.id} - β¦ `;
+ const valueSpan = li.querySelector('.value');
+
+ testsDetailsDiv.appendChild(li);
+
+ try {
+ const result = test.run();
+
+ if (result instanceof Promise) {
+ result
+ .then(data => {
+ valueSpan.innerHTML = resultToHTML(data);
+ resultObj.value = Boolean(data);
+ })
+ .catch(e => {
+ failed++;
+ valueSpan.innerHTML = `β error thrown ("${e.message ? e.message : e}")`;
+ updateSummary();
+ });
+ } else {
+ valueSpan.innerHTML = resultToHTML(result);
+ resultObj.value = Boolean(result);
+ }
+ } catch (e) {
+ failed++;
+ valueSpan.innerHTML = `β οΈ error thrown ("${e.message ? e.message : e}")`;
+ }
+
+ all++;
+ }
+
+ updateSummary();
+
+ startButton.removeAttribute('disabled');
+}
+
+function downloadTheResults () {
+ const data = JSON.stringify(results, null, 2);
+ const a = document.createElement('a');
+ const url = window.URL.createObjectURL(new Blob([data], { type: 'application/json' }));
+ a.href = url;
+ a.download = `${PAGE_NAME}-results.json`;
+
+ document.body.appendChild(a);
+ a.click();
+
+ window.URL.revokeObjectURL(url);
+ a.remove();
+}
+
+downloadButton.addEventListener('click', () => downloadTheResults());
+
+// run tests if button was clicked orβ¦
+startButton.addEventListener('click', () => runTests());
+
+// if url query is '?run'
+if (document.location.search === '?run') {
+ runTests();
+}
diff --git a/features/harmful-apis/style.css b/features/harmful-apis/style.css
new file mode 100644
index 0000000..36a5735
--- /dev/null
+++ b/features/harmful-apis/style.css
@@ -0,0 +1,3 @@
+* {
+ box-sizing: border-box;
+}
diff --git a/index.html b/index.html
index a5deb93..5084185 100644
--- a/index.html
+++ b/index.html
@@ -46,6 +46,7 @@ Browser Features
JS alerts and Hanging
Local storage
Client Hints
+ Harmful APIs
Element Hiding