Skip to content

Commit a6d1665

Browse files
Privacy 2025 (#172)
* cleanup * generalized fingerprints * unescape * more APIs * classify API * test fingerprintjs * cleanup * remove fingerprints * sync * async fetch * no promise * fix
1 parent c37f153 commit a6d1665

File tree

1 file changed

+7
-210
lines changed

1 file changed

+7
-210
lines changed

dist/privacy.js

Lines changed: 7 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const fetchAndParse = async (url, parser) => {
3838

3939
try {
4040
const response = await fetch(url, { signal });
41-
return parser(response);
41+
return parser(url, await response);
4242
} catch (error) {
4343
return {
4444
status: -1,
@@ -48,28 +48,20 @@ const fetchAndParse = async (url, parser) => {
4848
}
4949
};
5050

51-
/**
52-
* Checks if the response URL ends with any of the specified endings and if the response is OK.
53-
* @param {Response} response - The fetch response object.
54-
* @param {string[]} endings - An array of URL endings to check against.
55-
* @returns {boolean} - Returns true if the response is OK and the URL ends with one of the specified endings.
56-
*/
57-
const isPresent = (response, endings) => response.ok && endings.some(ending => response.url.endsWith(ending));
58-
5951
/**
6052
* Parses the response from a DSR delete request.
6153
* @param {Response} response - The response object from the fetch request.
6254
* @returns {Promise<Object>} A promise that resolves to an object containing the parsed response data.
6355
*/
64-
const parseDSRdelete = async (response) => {
56+
const parseDSRdelete = (url, response) => {
6557
let result = {
66-
present: isPresent(response, ['/dsrdelete.json']),
58+
present: response.ok && response.url.endsWith(url) && response.headers.get('content-type') === 'application/json',
6759
status: response.status,
6860
};
6961
Object.assign(result, result.present ? { redirected: response.redirected } : {});
7062

7163
try {
72-
let content = JSON.parse(await response.text());
64+
let content = JSON.parse(response.text());
7365
if (result.present && content) {
7466
for (const element of content.identifiers) {
7567
delete element.id;
@@ -83,94 +75,10 @@ const parseDSRdelete = async (response) => {
8375
Object.assign(result, result.present ? { error: error.message } : {});
8476
}
8577

86-
return Promise.resolve(result);
78+
return result;
8779
}
8880

8981
let sync_metrics = {
90-
/**
91-
* Privacy policies
92-
* Wording sourced from: https://github.com/RUB-SysSec/we-value-your-privacy/blob/master/privacy_wording.json
93-
* words = privacy_wording.map(country => country.words).filter((v, i, a) => a.indexOf(v) === i).flat().sort().join('|');
94-
*/
95-
privacy_wording_links: (() => {
96-
const languageKeywords = {
97-
af: "beskyttelse af personlige oplysninger|privatlivspolitik|persondata",
98-
ar: "الخصوصية|سياسة البيانات|سياسة الخصوصية|سياسة الخصوصية والبيانات",
99-
az: "məxfilik|şəxsi məlumatlar",
100-
be: "абарона дадзеных|палітыка прыватнасці",
101-
bg: "поверителност|политика за бисквитки|политика за данни|условия|условия за ползване|политика за поверителност",
102-
bn: "গোপনীয়তা|ডেটা নীতি|গোপনীয়তা নীতি",
103-
bs: "privatnost|politika privatnosti|politika podataka|pravila o privatnosti",
104-
ca: "protecció de dades|política de privacitat",
105-
cs: "ochrana dat|ochrana osobních údajů|ochrana soukromí|ochrana súkromia|ochrana udaju|ochrana údajov|ochrany osobných údajov|podmínky|soukromi|soukromí|zásady používání dat|zásady používání cookies",
106-
da: "cookiepolitik|datapolicy|beskyttelse af personlige oplysninger|personlige data|personoplysninger|privatlivspolitik|regler om fortrolighed",
107-
de: "datenrichtlinie|datenschutz|datenschutzbestimmungen|datenschutzrichtlinie|privatssphäre|cookie-richtlinie|privatsphärenerklärung",
108-
el: "απόρρητο|πολιτική απορρήτου|πολιτική δεδομένων|προσωπικά δεδομένα|όροι και γνωστοποιήσεις|πολιτική cookies",
109-
en: "cookie policy|cookies|data policy|datapolicy|privacy|privacy policy|cookiepolicy",
110-
es: "aviso legal|confidencialidad|confidencialite|confidentialité|política de datos|privacidad|privacidad|politica de datos|política de privacidad|política de cookies",
111-
et: "andmekaitsetingimused|isikuandmete|isikuandmete töötlemise|kasutustingimused|privaatsuspoliitika|andmepoliitika|küpsisepoliitika",
112-
eu: "privatua|datu pertsonalen babesa|datu pertsonalen politika",
113-
fa: "حریم خصوصی|سیاست حفظ حریم خصوصی|سیاست داده|داده های شخصی",
114-
fi: "yksityisyyden suoja|yksityisyydensuoja|yksityisyys|tietokäytäntö|tietosuoja|tietosuojakäytäntö|tietosuojaseloste|evästekäytäntö",
115-
fil: "patakaran sa cookies",
116-
fr: "cgu|cgv|confidentialité|mentions légales|politique d’utilisation des données|rgpd|vie privée|politique de confidentialité|politique de données|politique de cookie",
117-
ga: "beartas príobháideachta|beartas sonraí|beartas fianán|beartas sonraí pearsanta",
118-
he: "מדיניות נתונים|פרטיות",
119-
hi: "गोपनीयता|डेटा नीति|गोपनीयता नीति",
120-
hr: "privatnost|pravila o privatnosti|pravila o podacima|pravila o kolačićima",
121-
hu: "adatvédelem|adatvédelmi|személyes adatok védelme|adatvédelmi nyilatkozat|adatkezelési tájékoztató|cookie-kra vonatkozó irányelv",
122-
id: "integritetspolicy|piškotki|kebijakan privasi",
123-
is: "persónuvernd|persónuverndarstefna",
124-
it: "normativa sui dati|privatezza|informativa sulla privacy|informativa sui dati|informativa sui cookie|politica dei dati|politica dei cookies",
125-
ja: "プライバシー|データポリシー|個人情報保護",
126-
ko: "개인정보|개인정보 처리방침|개인정보 보호정책|개인정보 보호|정보 처리 방침",
127-
ka: "კერძო წამყვანი|პირადი ინფორმაციის დაცვა|პირადი ინფორმაციის პოლიტიკა",
128-
lt: "privatumas|privatumo|slapukai|slapukkih|privatumo politika|duomenų politika|slapukų politika|privatumo pareiškimas",
129-
lv: "sīkdatne|sīkdatņu|privātuma|privātums|privātuma politika|datu politika|sīkdatņu politika|privātuma politikas paziņojums",
130-
mt: "politika dwar il-privatezza|politika tad-data|politika tal-cookies|politika dwar id-dati",
131-
ms: "privasi|polisi data|polisi privasi|data peribadi|terma dan syarat",
132-
nb: "personvern|informasjonskapselregler",
133-
nl: "gegevensbeleid|privacybeleid|cookiebeleid|privacyverklaring",
134-
no: "personvern|personvernerklæring|informasjonskapsler|personvernspolicy",
135-
pl: "prywatnosci|prywatności|prywatność|zasady dotyczące danych|polityka prywatności|polityka danych|polityka plików cookie",
136-
pt: "privacidade|política de privacidade|política de dados|política de cookies",
137-
ro: "confidențialitate|politica de utilizare|protectia datelor|politica de confidențialitate|politica de date|politica cookie",
138-
ru: "конфиденциальность|политика использования данных|политика конфиденциальности|политика данных|политика файлов cookie|персональных данных",
139-
si: "piškotki",
140-
sk: "ochrana osobných údajov|zásady ochrany osobných|zásady používání dat|zásady využívania údajov|zásady ochrany osobných údajov|zásady používania údajov|zásady používania cookies|ochrana údajov",
141-
sl: "piškotki|varstvo podatkov|zasebnost|pravilnik o zasebnosti|pravilnik o podatkih|pravilnik o piškotkih|politika zasebnosti",
142-
sq: "konfidencialiteti|politika e privatësisë|politika e të dhënave personale",
143-
sr: "konfidentsiaalsuse|pravila o upotrebi podataka|privatnost|privatnosti|prywatnosci|prywatności|prywatność|protecţia datelor|политика о подацима|приватност|защита података",
144-
sv: "integritetspolicy|personuppgifter|privatlivspolitik|sekretess|webbplatsen|yksityisyyden suoja|yksityisyydensuoja|yksityisyys|datapolitik",
145-
sw: "política de datos",
146-
tr: "gizlilik|kişisel verilerin korunması|politika e të dhënave|politikat e privatesise|politikat e privatësisë|veri i̇lkesi|veri politikası|gizlilik politikası|veri politikası|çerez politikası",
147-
th: "ความเป็นส่วนตัว|นโยบายความเป็นส่วนตัว|นโยบายข้อมูล|ข้อมูลส่วนบุคคล|เงื่อนไข",
148-
vi: "quyền riêng tư|chính sách bảo mật|chính sách dữ liệu|dữ liệu cá nhân|điều khoản và điều kiện",
149-
uk: "конфіденційність|конфіденційності|політика даних|файлів cookie|персональних даних|захисту даних",
150-
zh: "数据使用政策|隐私政策|数据保护政策|隐私保护政策|數據使用政策|隱私政策|數據保護政策|隱私保護政策"
151-
}
152-
const websiteLanguage = document.documentElement.lang.slice(0, 2).toLowerCase();
153-
let keywords;
154-
if (websiteLanguage == 'en') {
155-
keywords = languageKeywords[websiteLanguage]
156-
} else if (!(websiteLanguage in languageKeywords)) {
157-
keywords = Object.values(languageKeywords).join('|');
158-
} else {
159-
keywords = languageKeywords[websiteLanguage] + '|' + languageKeywords['en']
160-
}
161-
const pattern = new RegExp(`(?:${keywords})`, 'gi');
162-
163-
const privacy_links = Array.from(document.querySelectorAll('a')).filter(a =>
164-
pattern.test(a.innerText)
165-
).map(
166-
a => ({
167-
text: a.innerText,
168-
})
169-
);
170-
171-
return privacy_links;
172-
})(),
173-
17482
// Consent Management Platforms
17583

17684
/**
@@ -250,7 +158,7 @@ let sync_metrics = {
250158
})(),
251159

252160
/**
253-
* Global Privacy Platfrom (GPP)
161+
* Global Privacy Protocol (GPP)
254162
* https://github.com/InteractiveAdvertisingBureau/Global-Privacy-Platform
255163
*/
256164
iab_gpp: (() => {
@@ -380,114 +288,6 @@ let sync_metrics = {
380288
return rp;
381289
})(),
382290

383-
/**
384-
* Media devices
385-
* https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices
386-
*/
387-
media_devices: {
388-
navigator_mediaDevices_enumerateDevices: testPropertyStringInResponseBodies(
389-
'mediaDevices.+enumerateDevices'
390-
),
391-
navigator_mediaDevices_getUserMedia: testPropertyStringInResponseBodies(
392-
'mediaDevices.+getUserMedia'
393-
),
394-
navigator_mediaDevices_getDisplayMedia: testPropertyStringInResponseBodies(
395-
'mediaDevices.+getDisplayMedia'
396-
),
397-
},
398-
399-
/**
400-
* Geolocation API
401-
* https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API
402-
*/
403-
geolocation: {
404-
navigator_geolocation_getCurrentPosition: testPropertyStringInResponseBodies(
405-
'geolocation.+getCurrentPosition'
406-
),
407-
navigator_geolocation_watchPosition: testPropertyStringInResponseBodies(
408-
'geolocation.+watchPosition'
409-
),
410-
},
411-
412-
fingerprinting: (() => {
413-
//These are determined by looking at the tests in https://github.com/fingerprintjs/fingerprintjs
414-
const fingerprintingAPIs = [
415-
'ApplePaySession.canMakePayments',
416-
'getChannelData', //audioContext
417-
'toDataURL', //canvas
418-
'getImageData', //canvas, not actually used by fingerprintJS
419-
'screen.colorDepth',
420-
'color-gamut',
421-
'prefers-contrast',
422-
'cpuClass',
423-
'deviceMemory',
424-
'forced-colors',
425-
'hardwareConcurrency',
426-
'dynamic-range',
427-
'indexedDB',
428-
'inverted-colors',
429-
'navigator.language', //"language" would be too generic here
430-
'navigator.userLanguage', //TODO exists?
431-
'localStorage',
432-
'min-monochrome',
433-
'max-monochrome',
434-
'openDatabase',
435-
'navigator.oscpu',
436-
'pdfViewerEnabled',
437-
'navigator.platform', //"platform" would be too generic
438-
'navigator.plugins',
439-
'attributionSourceId',
440-
'prefers-reduced-motion',
441-
'prefers-reduced-transparency',
442-
'availWidth',
443-
'availHeight',
444-
'screen.width',
445-
'screen.height',
446-
'sessionStorage',
447-
'resolvedOptions().timeZone',
448-
'getTimezoneOffset',
449-
'maxTouchPoints',
450-
'ontouchstart',
451-
'navigator.vendor',
452-
'vendorUnmasked',
453-
'rendererUnmasked',
454-
'shadingLanguageVersion',
455-
'WEBGL_debug_renderer_info',
456-
'getShaderPrecisionFormat'
457-
].map(api => api.toLowerCase())
458-
459-
const response_bodies = $WPT_BODIES.filter(body => (body.response_body && (body.type === 'Document' || body.type === 'Script')))
460-
461-
let fingerprintingUsageCounts = {}
462-
let likelyFingerprintingScripts = []
463-
464-
response_bodies.forEach(req => {
465-
let total_occurrences = 0
466-
467-
let body = req.response_body.toLowerCase()
468-
469-
fingerprintingAPIs.forEach(api => {
470-
let api_occurrences = 0
471-
let index = body.indexOf(api)
472-
while (index !== -1) {
473-
api_occurrences++
474-
index = body.indexOf(api, index + 1)
475-
}
476-
477-
if (api_occurrences > 0) {
478-
fingerprintingUsageCounts[api] = (fingerprintingUsageCounts[api] || 0) + api_occurrences
479-
}
480-
total_occurrences += api_occurrences
481-
})
482-
483-
if (total_occurrences >= 5) { //TODO what should this threshold be?
484-
likelyFingerprintingScripts.push(req.url)
485-
}
486-
})
487-
488-
return { counts: fingerprintingUsageCounts, likelyFingerprintingScripts }
489-
})(),
490-
491291
/**
492292
* List of hostnames with CNAME record
493293
*/
@@ -576,10 +376,7 @@ let sync_metrics = {
576376

577377
let CCPAdata = {
578378
hasCCPALink: CCPALinks.length > 0,
579-
}
580-
if (CCPAdata.hasCCPALink) {
581-
CCPAdata.CCPALinkPhrases = CCPALinks.map(link => link.textContent.trim().toLowerCase())
582-
}
379+
};
583380

584381
return CCPAdata
585382
})()

0 commit comments

Comments
 (0)