From b6973eb4370b3f44a502f857c6293e24ccb1bcd2 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 5 Jun 2024 22:56:48 +0200 Subject: [PATCH] port stuff to ts --- assets/js/fp.js | 112 ------------------------- assets/js/fp.ts | 163 ++++++++++++++++++++++++++++++++++++ assets/js/when-ready.js | 4 +- assets/test/vitest-setup.ts | 3 +- 4 files changed, 167 insertions(+), 115 deletions(-) delete mode 100644 assets/js/fp.js create mode 100644 assets/js/fp.ts diff --git a/assets/js/fp.js b/assets/js/fp.js deleted file mode 100644 index 9ee3a03d..00000000 --- a/assets/js/fp.js +++ /dev/null @@ -1,112 +0,0 @@ -/** - * FP version 4 - * - * Not reliant on deprecated properties, - * and potentially more accurate at what it's supposed to do. - */ - -import { $ } from './utils/dom'; -import store from './utils/store'; - -// http://stackoverflow.com/a/34842797 -function hashCode(str) { - return str.split('').reduce((prevHash, currVal) => - ((prevHash << 5) - prevHash) + currVal.charCodeAt(0), 0) >>> 0; -} - -async function createFp() { - let kb = 'none'; - let mem = '1'; - let ua = 'none'; - - if (navigator.keyboard) { - kb = (await navigator.keyboard.getLayoutMap()).entries().toArray().sort().map(e => `${e[0]}${e[1]}`).join(''); - } - - if (navigator.deviceMemory) { - mem = navigator.deviceMemory.toString(); - } - - if (navigator.userAgentData) { - const uadata = navigator.userAgentData; - let brands = 'none'; - - if (uadata.brands && uadata.brands.length > 0) { - brands = uadata.brands.filter(e => !e.brand.match(/.*ot.*rand.*/gi)).map(e => `${e.brand}${e.version}`).join(''); - } - - ua = `${brands}${uadata.mobile}${uadata.platform}`; - } - - let width = store.get('cached_rem_size'); - const body = $('body'); - - if (!width && body) { - const testElement = document.createElement('span'); - testElement.style.minWidth = '1rem'; - testElement.style.maxWidth = '1rem'; - testElement.style.position = 'absolute'; - - body.appendChild(testElement); - - width = testElement.clientWidth.toString(); - - body.removeChild(testElement); - - store.set('cached_rem_size', width); - } - - if (!width) { - width = '0'; - } - - const prints = [ - navigator.userAgent, - navigator.hardwareConcurrency.toString(), - navigator.maxTouchPoints.toString(), - navigator.language, - kb, - mem, - ua, - width, - - screen.height.toString(), - screen.width.toString(), - screen.colorDepth.toString(), - screen.pixelDepth.toString(), - - window.devicePixelRatio.toString(), - new Date().getTimezoneOffset().toString(), - ]; - - return hashCode(prints.join('')); -} - -async function setFpCookie() { - let fp = store.get('cached_ses_value'); - - if (!fp) { - const m = document.cookie.match(/_ses=([a-f\d]+)/); - - if (m && m[1]) { - fp = m[1]; - } - } - - if (!fp || fp.charAt(0) !== 'd') { - // The prepended 'd' acts as a crude versioning mechanism. - try { - fp = `d${await createFp()}`; - } - // If it fails, use fakeprint "d1836832948" as a last resort. - catch (err) { - fp = 'd1836832948'; - } - - store.set('cached_ses_value', fp); - } - - document.cookie = `_ses=${fp}; path=/; SameSite=Lax`; -} - -export { setFpCookie }; diff --git a/assets/js/fp.ts b/assets/js/fp.ts new file mode 100644 index 00000000..84a1557b --- /dev/null +++ b/assets/js/fp.ts @@ -0,0 +1,163 @@ +/** + * FP version 4 + * + * Not reliant on deprecated properties, + * and potentially more accurate at what it's supposed to do. + */ + +import { $ } from './utils/dom'; +import store from './utils/store'; + +interface RealKeyboard { + getLayoutMap: () => Promise> +} + +interface RealUserAgentData { + brands: [{brand: string, version: string}], + mobile: boolean, + platform: string, +} + +interface RealNavigator extends Navigator { + deviceMemory: number, + keyboard: RealKeyboard, + userAgentData: RealUserAgentData, +} + +/** + * Creates a 53-bit long hash of a string. + * + * @param {string} str The string to hash. + * @param {number} seed The seed to use for hash generation. + * @return {number} The resulting hash as a 53-bit number. + * @see {@link https://stackoverflow.com/a/8831937} + */ +function cyrb53(str: string, seed: number = 0x16fe7b0a): number { + let h1 = 0xdeadbeef ^ seed; + let h2 = 0x41c6ce57 ^ seed; + + for (let i = 0, ch; i < str.length; i++) { + ch = str.charCodeAt(i); + h1 = Math.imul(h1 ^ ch, 2654435761); + h2 = Math.imul(h2 ^ ch, 1597334677); + } + + h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507); + h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909); + h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507); + h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909); + + return 4294967296 * (2097151 & h2) + (h1 >>> 0); +} + +/** Creates a semi-unique string from browser attributes. + * + * @async + * @return {Promise} Hexadecimally encoded 53 bit number padded to 7 bytes. + */ +async function createFp(): Promise { + const nav = navigator as RealNavigator; + let kb = 'none'; + let mem = '1'; + let ua = 'none'; + + if (nav.keyboard) { + kb = Array.from((await nav.keyboard.getLayoutMap()).entries()).sort().map(e => `${e[0]}${e[1]}`).join(''); + } + + if (nav.deviceMemory) { + mem = nav.deviceMemory.toString(); + } + + if (nav.userAgentData) { + const uadata = nav.userAgentData; + let brands = 'none'; + + if (uadata.brands && uadata.brands.length > 0) { + brands = uadata.brands.filter(e => !e.brand.match(/.*ot.*rand.*/gi)).map(e => `${e.brand}${e.version}`).join(''); + } + + ua = `${brands}${uadata.mobile}${uadata.platform}`; + } + + let width: string | null = store.get('cached_rem_size'); + const body = $('body'); + + if (!width && body) { + const testElement = document.createElement('span'); + testElement.style.minWidth = '1rem'; + testElement.style.maxWidth = '1rem'; + testElement.style.position = 'absolute'; + + body.appendChild(testElement); + + width = testElement.clientWidth.toString(); + + body.removeChild(testElement); + + store.set('cached_rem_size', width); + } + + if (!width) { + width = '0'; + } + + const prints: string[] = [ + navigator.userAgent, + navigator.hardwareConcurrency.toString(), + navigator.maxTouchPoints.toString(), + navigator.language, + kb, + mem, + ua, + width, + + screen.height.toString(), + screen.width.toString(), + screen.colorDepth.toString(), + screen.pixelDepth.toString(), + + window.devicePixelRatio.toString(), + new Date().getTimezoneOffset().toString(), + ]; + + return cyrb53(prints.join('')).toString(16).padStart(14, '0'); +} + +/** + * Sets the `_ses` cookie. + * + * If `cached_ses_value` is present in local storage, uses that instead. + * Otherwise if the `_ses` cookie already exits, uses its value instead. + * Otherwise attempts to generate a new value for the `_ses` cookie + * based on various browser attributes. + * Failing all previous methods, sets the `_ses` cookie to a fallback value. + * + * @async + */ +export async function setSesCookie() { + let fp: string | null = store.get('cached_ses_value'); + + if (!fp) { + const m = document.cookie.match(/_ses=([a-f0-9]+)/); + + if (m && m[1]) { + fp = m[1]; + } + } + + if (!fp || fp.charAt(0) !== 'd' || fp.length !== 15) { + // The prepended 'd' acts as a crude versioning mechanism. + try { + fp = `d${await createFp()}`; + } + // If it fails, use fakeprint "d015c342859dde3" as a last resort. + catch (err) { + fp = 'd015c342859dde3'; + } + + store.set('cached_ses_value', fp); + } + + document.cookie = `_ses=${fp}; path=/; SameSite=Lax`; +} diff --git a/assets/js/when-ready.js b/assets/js/when-ready.js index 2eb90aa6..f39cec69 100644 --- a/assets/js/when-ready.js +++ b/assets/js/when-ready.js @@ -14,7 +14,7 @@ import { setupBurgerMenu } from './burger'; import { bindCaptchaLinks } from './captcha'; import { setupComments } from './comment'; import { setupDupeReports } from './duplicate_reports'; -import { setFpCookie } from './fp'; +import { setSesCookie } from './fp'; import { setupGalleryEditing } from './galleries'; import { initImagesClientside } from './imagesclientside'; import { bindImageTarget } from './image_expansion'; @@ -50,7 +50,7 @@ whenReady(() => { initImagesClientside(); setupComments(); setupDupeReports(); - setFpCookie(); + setSesCookie(); setupGalleryEditing(); bindImageTarget(); setupEvents(); diff --git a/assets/test/vitest-setup.ts b/assets/test/vitest-setup.ts index d889b27f..e4cd1314 100644 --- a/assets/test/vitest-setup.ts +++ b/assets/test/vitest-setup.ts @@ -20,7 +20,8 @@ window.booru = { hiddenFilter: matchNone(), spoileredFilter: matchNone(), interactions: [], - tagsVersion: 5 + tagsVersion: 5, + hideStaffTools: 'false' }; // https://github.com/jsdom/jsdom/issues/1721#issuecomment-1484202038