|
725 | 725 | // eslint-disable-next-line no-global-assign |
726 | 726 | const globalObj = typeof window === 'undefined' ? globalThis : window; |
727 | 727 |
|
| 728 | + /** |
| 729 | + * @param {string} sessionKey |
| 730 | + * @param {string} domainKey |
| 731 | + * @param {number} inputData |
| 732 | + */ |
728 | 733 | function getDataKeySync (sessionKey, domainKey, inputData) { |
729 | 734 | // eslint-disable-next-line new-cap |
730 | 735 | const hmac = new sjcl.misc.hmac(sjcl.codec.utf8String.toBits(sessionKey + domainKey), sjcl.hash.sha256); |
731 | 736 | return sjcl.codec.hex.fromBits(hmac.encrypt(inputData)) |
732 | 737 | } |
733 | 738 |
|
734 | | - // linear feedback shift register to find a random approximation |
| 739 | + /** |
| 740 | + * Linear feedback shift register to find a random approximation |
| 741 | + * @param {number} v |
| 742 | + */ |
735 | 743 | function nextRandom (v) { |
736 | 744 | return Math.abs((v >> 1) | (((v << 62) ^ (v << 61)) & (~(~0 << 63) << 62))) |
737 | 745 | } |
738 | 746 |
|
739 | 747 | const exemptionLists = {}; |
| 748 | + /** |
| 749 | + * @param {string | number} type |
| 750 | + * @param {string} url |
| 751 | + */ |
740 | 752 | function shouldExemptUrl (type, url) { |
741 | 753 | for (const regex of exemptionLists[type]) { |
742 | 754 | if (regex.test(url)) { |
|
748 | 760 |
|
749 | 761 | let debug = false; |
750 | 762 |
|
| 763 | + /** |
| 764 | + * @param {{ debug?: any; stringExemptionLists?: any; }} args |
| 765 | + */ |
751 | 766 | function initStringExemptionLists (args) { |
752 | 767 | const { stringExemptionLists } = args; |
753 | 768 | debug = args.debug; |
|
759 | 774 | } |
760 | 775 | } |
761 | 776 |
|
762 | | - // Checks the stack trace if there are known libraries that are broken. |
| 777 | + /** |
| 778 | + * Checks the stack trace if there are known libraries that are broken. |
| 779 | + * @param {string} type |
| 780 | + */ |
763 | 781 | function shouldExemptMethod (type) { |
764 | 782 | // Short circuit stack tracing if we don't have checks |
765 | 783 | if (!(type in exemptionLists) || exemptionLists[type].length === 0) { |
766 | 784 | return false |
767 | 785 | } |
768 | 786 | try { |
769 | | - const errorLines = new Error().stack.split('\n'); |
| 787 | + const errorLines = new Error().stack?.split('\n') || []; |
770 | 788 | const errorFiles = new Set(); |
771 | 789 | // Should cater for Chrome and Firefox stacks, we only care about https? resources. |
772 | 790 | const lineTest = /(\()?(http[^)]+):[0-9]+:[0-9]+(\))?/; |
|
790 | 808 | return false |
791 | 809 | } |
792 | 810 |
|
793 | | - // Iterate through the key, passing an item index and a byte to be modified |
| 811 | + /** |
| 812 | + * Iterate through the key, passing an item index and a byte to be modified |
| 813 | + * @param {any} key |
| 814 | + * @param {{ (item: any, byte: any): void; (arg0: any, arg1: any): any; }} callback |
| 815 | + */ |
794 | 816 | function iterateDataKey (key, callback) { |
795 | 817 | let item = key.charCodeAt(0); |
796 | 818 | for (const i in key) { |
|
811 | 833 | } |
812 | 834 | } |
813 | 835 |
|
| 836 | + /** |
| 837 | + * @param {{ site: { isBroken: any; allowlisted: any; enabledFeatures: string | any[]; }; }} args |
| 838 | + * @param {string} feature |
| 839 | + */ |
814 | 840 | function isFeatureBroken (args, feature) { |
815 | 841 | return args.site.isBroken || args.site.allowlisted || !args.site.enabledFeatures.includes(feature) |
816 | 842 | } |
817 | 843 |
|
818 | 844 | /** |
819 | 845 | * For each property defined on the object, update it with the target value. |
| 846 | + * @param {string} name |
| 847 | + * @param {{ object: any; origValue: any; targetValue: any; }} prop |
820 | 848 | */ |
821 | 849 | function overrideProperty (name, prop) { |
822 | 850 | // Don't update if existing value is undefined or null |
|
840 | 868 | return prop.origValue |
841 | 869 | } |
842 | 870 |
|
| 871 | + /** |
| 872 | + * @param {typeof globalThis} object |
| 873 | + * @param {PropertyKey} propertyName |
| 874 | + * @param {PropertyDescriptor & ThisType<any>} descriptor |
| 875 | + */ |
843 | 876 | function defineProperty (object, propertyName, descriptor) { |
844 | 877 | { |
845 | 878 | Object.defineProperty(object, propertyName, descriptor); |
846 | 879 | } |
847 | 880 | } |
848 | 881 |
|
| 882 | + /** |
| 883 | + * @param {string} dashCaseText |
| 884 | + */ |
849 | 885 | function camelcase (dashCaseText) { |
850 | | - return dashCaseText.replace(/-(.)/g, (match, letter) => { |
| 886 | + return dashCaseText.replace(/-(.)/g, (/** @type {any} */ match, /** @type {string} */ letter) => { |
851 | 887 | return letter.toUpperCase() |
852 | 888 | }) |
853 | 889 | } |
|
874 | 910 | return result === 'enabled' |
875 | 911 | } |
876 | 912 |
|
| 913 | + /** |
| 914 | + * @template {object} P |
| 915 | + * @typedef {(target: object, thisArg: P, args: object) => void} ApplyMethod<P> |
| 916 | + */ |
| 917 | + |
877 | 918 | /** |
878 | 919 | * @template {object} P |
879 | 920 | * @typedef {object} ProxyObject<P> |
880 | | - * @property {(target?: object, thisArg?: P, args?: object) => void} apply |
| 921 | + * @property {ApplyMethod<P>} apply? |
881 | 922 | */ |
882 | 923 |
|
883 | 924 | /** |
|
895 | 936 | this.property = property; |
896 | 937 | this.featureName = featureName; |
897 | 938 | this.camelFeatureName = camelcase(this.featureName); |
| 939 | + /** @type ApplyMethod<P> */ |
898 | 940 | const outputHandler = (...args) => { |
899 | 941 | const isExempt = shouldExemptMethod(this.camelFeatureName); |
900 | 942 | if (debug) { |
901 | 943 | postDebugMessage(this.camelFeatureName, { |
902 | 944 | action: isExempt ? 'ignore' : 'restrict', |
903 | 945 | kind: this.property, |
904 | 946 | documentUrl: document.location.href, |
905 | | - stack: new Error().stack, |
| 947 | + stack: new Error().stack || '', |
906 | 948 | args: JSON.stringify(args[2]) |
907 | 949 | }); |
908 | 950 | } |
|
928 | 970 | } |
929 | 971 | } |
930 | 972 |
|
| 973 | + /** |
| 974 | + * @param {any} feature |
| 975 | + * @param {{ action: string; kind: string; documentUrl: string; stack: string; args: string; }} message |
| 976 | + */ |
931 | 977 | function postDebugMessage (feature, message) { |
932 | 978 | globalObj.postMessage({ |
933 | 979 | action: feature, |
|
1056 | 1102 | const featureName = 'fingerprinting-audio'; |
1057 | 1103 |
|
1058 | 1104 | // In place modify array data to remove fingerprinting |
1059 | | - function transformArrayData (channelData, domainKey, sessionKey, thisArg) { |
| 1105 | + function transformArrayData (channelData, domainKey, sessionKey, thisArg, args) { |
1060 | 1106 | let { audioKey } = getCachedResponse(thisArg, args); |
1061 | 1107 | if (!audioKey) { |
1062 | 1108 | let cdSum = 0; |
|
1164 | 1210 | * as well as prevent any script from listening to events. |
1165 | 1211 | */ |
1166 | 1212 | function init$b (args) { |
1167 | | - if (globalThis.navigator.getBattery) { |
| 1213 | + if ('getBattery' in globalThis.navigator) { |
1168 | 1214 | const BatteryManager = globalThis.BatteryManager; |
1169 | 1215 |
|
1170 | 1216 | const spoofedValues = { |
|
2157 | 2203 |
|
2158 | 2204 | var seedrandom = sr; |
2159 | 2205 |
|
| 2206 | + /** |
| 2207 | + * @typedef {CanvasRenderingContext2D | WebGL2RenderingContext | WebGLRenderingContext} CanvasContext |
| 2208 | + */ |
| 2209 | + |
2160 | 2210 | /** |
2161 | 2211 | * @param {HTMLCanvasElement} canvas |
2162 | 2212 | * @param {string} domainKey |
2163 | 2213 | * @param {string} sessionKey |
2164 | 2214 | * @param {any} getImageDataProxy |
2165 | | - * @param {CanvasRenderingContext2D | WebGL2RenderingContext | WebGLRenderingContext} ctx? |
| 2215 | + * @param {CanvasContext} ctx? |
| 2216 | + * @return {{offScreenCanvas: HTMLCanvasElement, offScreenCtx: CanvasContext}?} |
2166 | 2217 | */ |
2167 | 2218 | function computeOffScreenCanvas (canvas, domainKey, sessionKey, getImageDataProxy, ctx) { |
2168 | 2219 | if (!ctx) { |
2169 | | - ctx = canvas.getContext('2d'); |
| 2220 | + const newCtx = canvas.getContext('2d'); |
| 2221 | + if (newCtx === null) { |
| 2222 | + return null |
| 2223 | + } |
| 2224 | + ctx = newCtx; |
2170 | 2225 | } |
2171 | 2226 |
|
2172 | 2227 | // Make a off-screen canvas and put the data there |
2173 | 2228 | const offScreenCanvas = document.createElement('canvas'); |
2174 | 2229 | offScreenCanvas.width = canvas.width; |
2175 | 2230 | offScreenCanvas.height = canvas.height; |
2176 | 2231 | const offScreenCtx = offScreenCanvas.getContext('2d'); |
| 2232 | + if (offScreenCtx === null) { |
| 2233 | + return null |
| 2234 | + } |
2177 | 2235 |
|
2178 | 2236 | let rasterizedCtx = ctx; |
2179 | 2237 | // If we're not a 2d canvas we need to rasterise first into 2d |
|
2517 | 2575 |
|
2518 | 2576 | overrideProperty('keyboard', { |
2519 | 2577 | object: Navigator.prototype, |
| 2578 | + // @ts-ignore |
2520 | 2579 | origValue: navigator.keyboard, |
2521 | 2580 | targetValue: undefined |
2522 | 2581 | }); |
|
2527 | 2586 | }); |
2528 | 2587 | overrideProperty('deviceMemory', { |
2529 | 2588 | object: Navigator.prototype, |
| 2589 | + // @ts-ignore |
2530 | 2590 | origValue: navigator.deviceMemory, |
2531 | 2591 | targetValue: 8 |
2532 | 2592 | }); |
|
2587 | 2647 | setWindowPropertyValue('screenTop', normalizedY); |
2588 | 2648 | } |
2589 | 2649 |
|
2590 | | - if (top.window.outerHeight >= origPropertyValues.availHeight - 1) { |
2591 | | - setWindowPropertyValue('outerHeight', top.window.screen.height); |
| 2650 | + const outerHeight = top?.window.outerHeight || 0; |
| 2651 | + if (outerHeight >= origPropertyValues.availHeight - 1) { |
| 2652 | + setWindowPropertyValue('outerHeight', top?.window.screen.height); |
2592 | 2653 | } else { |
2593 | 2654 | try { |
2594 | | - setWindowPropertyValue('outerHeight', top.window.outerHeight); |
| 2655 | + setWindowPropertyValue('outerHeight', top?.window.outerHeight); |
2595 | 2656 | } catch (e) { |
2596 | 2657 | // top not accessible to certain iFrames, so ignore. |
2597 | 2658 | } |
|
2605 | 2666 | setWindowPropertyValue('screenLeft', normalizedX); |
2606 | 2667 | } |
2607 | 2668 |
|
2608 | | - if (top.window.outerWidth >= origPropertyValues.availWidth - 1) { |
2609 | | - setWindowPropertyValue('outerWidth', top.window.screen.width); |
| 2669 | + const outerWidth = top?.window.outerWidth || 0; |
| 2670 | + if (outerWidth >= origPropertyValues.availWidth - 1) { |
| 2671 | + setWindowPropertyValue('outerWidth', top?.window.screen.width); |
2610 | 2672 | } else { |
2611 | 2673 | try { |
2612 | | - setWindowPropertyValue('outerWidth', top.window.outerWidth); |
| 2674 | + setWindowPropertyValue('outerWidth', top?.window.outerWidth); |
2613 | 2675 | } catch (e) { |
2614 | 2676 | // top not accessible to certain iFrames, so ignore. |
2615 | 2677 | } |
|
2619 | 2681 | } |
2620 | 2682 | } |
2621 | 2683 |
|
| 2684 | + // @ts-ignore |
2622 | 2685 | function init$8 (args) { |
2623 | 2686 | const Screen = globalThis.Screen; |
2624 | 2687 | const screen = globalThis.screen; |
2625 | 2688 |
|
2626 | 2689 | origPropertyValues.availTop = overrideProperty('availTop', { |
2627 | 2690 | object: Screen.prototype, |
| 2691 | + // @ts-ignore |
2628 | 2692 | origValue: screen.availTop, |
2629 | 2693 | targetValue: 0 |
2630 | 2694 | }); |
2631 | 2695 | origPropertyValues.availLeft = overrideProperty('availLeft', { |
2632 | 2696 | object: Screen.prototype, |
| 2697 | + // @ts-ignore |
2633 | 2698 | origValue: screen.availLeft, |
2634 | 2699 | targetValue: 0 |
2635 | 2700 | }); |
|
0 commit comments