From 5da28b0f8771834ae208d61431d632875e9f8e7d Mon Sep 17 00:00:00 2001 From: Ruben Rodriguez Date: Thu, 8 Sep 2022 20:18:54 -0400 Subject: Updated extensions: * Upgraded Privacy Redirect to 1.1.49 and configured to use the 10 most reliable invidious instances * Removed ViewTube * Added torproxy@icecat.gnu based on 'Proxy toggle' extension * Added jShelter 0.11.1 * Upgraded LibreJS to 7.21.0 * Upgraded HTTPS Everywhere to 2021.7.13 * Upgraded SubmitMe to 1.9 --- .../wrappingS-ECMA-ARRAY.js | 694 +++++++++++++++++++++ 1 file changed, 694 insertions(+) create mode 100644 data/extensions/jsr@javascriptrestrictor/wrappingS-ECMA-ARRAY.js (limited to 'data/extensions/jsr@javascriptrestrictor/wrappingS-ECMA-ARRAY.js') diff --git a/data/extensions/jsr@javascriptrestrictor/wrappingS-ECMA-ARRAY.js b/data/extensions/jsr@javascriptrestrictor/wrappingS-ECMA-ARRAY.js new file mode 100644 index 0000000..b5f56b2 --- /dev/null +++ b/data/extensions/jsr@javascriptrestrictor/wrappingS-ECMA-ARRAY.js @@ -0,0 +1,694 @@ +/** \file + * \brief Wrappers for arrays from the ECMA standard library + * + * \author Copyright (C) 2020 Peter Hornak + * + * \license SPDX-License-Identifier: GPL-3.0-or-later + */ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +/** \file + * \ingroup wrappers + * + * This wrapper aims on prevention of microarchitectural attacks like Meltdown and Spectre. This + * code was originally a part of [ChromeZero](https://github.com/IAIK/ChromeZero). + * + * \bug The `subarray()` method always ruturns the full array. + * + * The wrappers support the following behaviour: + * + * * Offset: The content of the buffer is shifted by a fix offset. This is a faster method but can + * be removed. + * * Random mapping: All items are mapped randomly. This is a slower method but more reliable. + * + * \see , especially Sect. 5.1 + */ + + +/// \see This function was adopted from https://github.com/inexorabletash/polyfill/blob/master/typedarray.js under MIT licence. +function packIEEE754(v, ebits, fbits) { + var bias = (1 << (ebits - 1)) - 1; + + function roundToEven(n) { + var w = Math.floor(n), f = n - w; + if (f < 0.5) + return w; + if (f > 0.5) + return w + 1; + return w % 2 ? w + 1 : w; + } + + // Compute sign, exponent, fraction + var s, e, f; + if (v !== v) { + // NaN + // http://dev.w3.org/2006/webapi/WebIDL/#es-type-mapping + e = (1 << ebits) - 1; + f = Math.pow(2, fbits - 1); + s = 0; + } else if (v === Infinity || v === -Infinity) { + e = (1 << ebits) - 1; + f = 0; + s = (v < 0) ? 1 : 0; + } else if (v === 0) { + e = 0; + f = 0; + s = (1 / v === -Infinity) ? 1 : 0; + } else { + s = v < 0; + v = Math.abs(v); + + if (v >= Math.pow(2, 1 - bias)) { + // Normalized + e = Math.min(Math.floor(Math.log(v) / Math.LN2), 1023); + var significand = v / Math.pow(2, e); + if (significand < 1) { + e -= 1; + significand *= 2; + } + if (significand >= 2) { + e += 1; + significand /= 2; + } + var d = Math.pow(2, fbits); + f = roundToEven(significand * d) - d; + e += bias; + if (f / d >= 1) { + e += 1; + f = 0; + } + if (e > 2 * bias) { + // Overflow + e = (1 << ebits) - 1; + f = 0; + } + } else { + // Denormalized + e = 0; + f = roundToEven(v / Math.pow(2, 1 - bias - fbits)); + } + } + + // Pack sign, exponent, fraction + var bits = [], i; + for (i = fbits; i; i -= 1) { + bits.push(f % 2 ? 1 : 0); + f = Math.floor(f / 2); + } + for (i = ebits; i; i -= 1) { + bits.push(e % 2 ? 1 : 0); + e = Math.floor(e / 2); + } + bits.push(s ? 1 : 0); + bits.reverse(); + var str = bits.join(''); + + // Bits to bytes + var bytes = []; + while (str.length) { + bytes.unshift(parseInt(str.substring(0, 8), 2)); + str = str.substring(8); + } + return bytes; +} + +/// \see This function was adopted from https://github.com/inexorabletash/polyfill/blob/master/typedarray.js under MIT licence. +function unpackIEEE754(bytes, ebits, fbits) { + // Bytes to bits + var bits = [], i, j, b, str, + bias, s, e, f; + + for (i = 0; i < bytes.length; ++i) { + b = bytes[i]; + for (j = 8; j; j -= 1) { + bits.push(b % 2 ? 1 : 0); + b = b >> 1; + } + } + bits.reverse(); + str = bits.join(''); + + // Unpack sign, exponent, fraction + bias = (1 << (ebits - 1)) - 1; + s = parseInt(str.substring(0, 1), 2) ? -1 : 1; + e = parseInt(str.substring(1, 1 + ebits), 2); + f = parseInt(str.substring(1 + ebits), 2); + + // Produce number + if (e === (1 << ebits) - 1) { + return f !== 0 ? NaN : s * Infinity; + } else if (e > 0) { + // Normalized + return s * Math.pow(2, e - bias) * (1 + f / Math.pow(2, fbits)); + } else if (f !== 0) { + // Denormalized + return s * Math.pow(2, -(bias - 1)) * (f / Math.pow(2, fbits)); + } else { + return s < 0 ? -0 : 0; + } +} + +/// \see Function was adopted from https://github.com/inexorabletash/polyfill/blob/master/typedarray.js under MIT licence. +function unpackF64(b) { + return unpackIEEE754(b, 11, 52); +} + +/// \see Function was adopted from https://github.com/inexorabletash/polyfill/blob/master/typedarray.js under MIT licence. +function packF64(v) { + return packIEEE754(v, 11, 52); +} + +/// \see Function was adopted from https://github.com/inexorabletash/polyfill/blob/master/typedarray.js under MIT licence. +function unpackF32(b) { + return unpackIEEE754(b, 8, 23); +} + +/// \see Function was adopted from https://github.com/inexorabletash/polyfill/blob/master/typedarray.js under MIT licence. +function packF32(v) { + return packIEEE754(v, 8, 23); +} + +function constructDecorator(wrapped) { + return function () { + const res = wrapped.apply(originalF, arguments); + return replacementF(res); + } +} + +function offsetDecorator(wrapped, type, proxyRef, offsetF) { + return function () { + var ref = proxyRef; + let newArr = []; + // Create a copy of array + for (let i = 0; i < this.length; i++) { + newArr[i] = this[offsetF(i)] + } + // Shift to original + for (let i = 0; i < this.length; i++) { + this[i] = newArr[i]; + } + // Do func + let res; + if (type === 3) { + res = new this.__proto__.constructor(this)[wrapped.name.split(' ')[1]]() + } else { + res = wrapped.apply(this, arguments); + } + // Create copy of new arr + let secArr = []; + for (let i = 0; i < this.length; i++) { + secArr[i] = this[i]; + } + for (let i = 0; i < this.length; i++) { + this[offsetF(i)] = secArr[i]; + } + switch (type) { + case 0: + return ref; + case 1: + return replacementF(res); + case 2: + return res; + default: + return res + } + } +} + +function redefineNewArrayFunctions(target, offsetF) { + target['sort'] = offsetDecorator(target['sort'], 0, target, offsetF); + target['reverse'] = offsetDecorator(target['reverse'], 0, target, offsetF); + target['fill'] = offsetDecorator(target['fill'], 0, target, offsetF); + target['copyWithin'] = offsetDecorator(target['copyWithin'], 0, target, offsetF); + target['subarray'] = offsetDecorator(target['subarray'], 0, target, offsetF); + target['slice'] = offsetDecorator(target['slice'], 1, target, offsetF); + target['map'] = offsetDecorator(target['map'], 1, target, offsetF); + target['filter'] = offsetDecorator(target['filter'], 1, target, offsetF); + target['set'] = offsetDecorator(target['set'], 2, target, offsetF); + target['reduce'] = offsetDecorator(target['reduce'], 2, target, offsetF); + target['reduceRight'] = offsetDecorator(target['reduceRight'], 2, target, offsetF); + target['lastIndexOf'] = offsetDecorator(target['lastIndexOf'], 2, target, offsetF); + target['indexOf'] = offsetDecorator(target['indexOf'], 2, target, offsetF); + target['forEach'] = offsetDecorator(target['forEach'], 2, target, offsetF); + target['find'] = offsetDecorator(target['find'], 2, target, offsetF); + target['join'] = offsetDecorator(target['join'], 2, target, offsetF); + target['entries'] = offsetDecorator(target['entries'], 3, target, offsetF); + target['keys'] = offsetDecorator(target['keys'], 3, target, offsetF); + target['values'] = offsetDecorator(target['values'], 3, target, offsetF); +} + +function redefineNewArrayConstructors(target) { + target['from'] = constructDecorator(originalF['from']); + target['of'] = constructDecorator(originalF['of']); +} + +/// Default proxy handler for Typed Arrays +var proxyHandler = `{ + get(target, key, receiver) { + var random_idx = Math.floor(Math.random() * target['length']); + // Load random index from array + var rand_val = target[random_idx]; + /* + let proto_keys = ['buffer', 'byteLength', 'byteOffset', 'length']; + if (proto_keys.indexOf(key) >= 0) { + return target[key]; + } + */ + // offsetF argument needs to be in array range + if (typeof key !== 'symbol' && Number(key) >= 0 && Number(key) < target.length) { + key = offsetF(key) + } + let value = target[key] + return typeof value == 'function' ? value.bind(target) : value; + }, + set(target, key, value) { + var random_idx = Math.floor(Math.random() * (target['length'])); + // Load random index from array + var rand_val = target[random_idx]; + rand_val = rand_val; + if (typeof key !== 'symbol' && Number(key) >= 0 && Number(key) < target.length) { + key = offsetF(key) + } + return target[key] = value; + } +}`; + +function getByteDecorator(wrapped, offsetF, name, doMapping) { + return function () { + const originalIdx = arguments[0]; + const endian = arguments[1]; + if (name === 'getUint8') { + // Random access + let ran = wrapped.apply(this, [Math.floor(Math.random() * (this.byteLength))]); + // Call original func + arguments[0] = offsetF(originalIdx); + return wrapped.apply(this, arguments); + } + if (!doMapping){ + this.getUint8(0); + return wrapped.apply(this, arguments); + } + const byteNumber = (parseInt(name[name.length - 2] + name[name.length - 1]) || parseInt(name[name.length - 1])) / 8; + let res = 0; + let swapNumber = byteNumber - 1; + for (let i = 0; i < byteNumber; i++) { + if (endian) { + // Shift starting with 0,1,2 + swapNumber = i * 2; + } + res += this.getUint8(originalIdx + i) << ((swapNumber - i) * 8); + } + return res; + } +} + +function setByteDecorator(wrapped, offsetF, name, doMapping) { + function toNBitBin(n, bits) { + if (n < 0) { + n = 0xFFFFFFFF + n + 1; + } + function makeString(n) { + let s = ""; + for (let i = 0; i < n; i++) { + s += "0"; + } + return s; + } + return (makeString(bits) + parseInt(n, 10).toString(2)).substr(-bits); + } + + return function () { + if (!doMapping){ + this.getUint8(0); + return wrapped.apply(this, arguments); + } + const originalIdx = arguments[0]; + const value = arguments[1]; + const endian = arguments[2]; + if (name === 'setUint8') { + // Random access + this.getUint8(0); + // Call original func + arguments[0] = offsetF(originalIdx); + return wrapped.apply(this, arguments); + } + const byteNumber = (parseInt(name[name.length - 2] + name[name.length - 1]) || parseInt(name[name.length - 1])) / 8; + const binNumber = toNBitBin(value, byteNumber * 8); + let numberPart; + for (let i = 0; i < byteNumber; i++) { + numberPart = binNumber.substr(i * 8, 8); + numberPart = parseInt(numberPart, 2); + if (endian) { + this.setUint8(originalIdx + byteNumber - i - 1, numberPart); + } else { + this.setUint8(originalIdx + i, numberPart); + } + } + return undefined; + } +} + +function getFloatDecorator(wrapped, name, doMapping) { + return function () { + if (!doMapping){ + this.getUint8(0); + return wrapped.apply(this, arguments); + } + const originalIdx = arguments[0]; + if (originalIdx === undefined) { + wrapped.apply(this, arguments) + } + const endian = arguments[1]; + const byteNumber = (parseInt(name[name.length - 2] + name[name.length - 1]) || parseInt(name[name.length - 1])) / 8; + let binArray = []; + // Random access + this.getUint8(0); + for (let i = 0; i < byteNumber; i++) { + binArray[binArray.length] = this.getUint8(originalIdx + i); + } + if (endian) { + binArray = binArray.reverse() + } + if (byteNumber === 4) { + return unpackF32(binArray); + } else { + return unpackF64(binArray); + } + } +} + +function setFloatDecorator(wrapped, name, doMapping) { + return function () { + if (!doMapping){ + this.getUint8(0); + return wrapped.apply(this, arguments); + } + const originalIdx = arguments[0]; + const value = arguments[1]; + if (originalIdx === undefined || value === undefined) { + wrapped.apply(this, arguments) + } + const endian = arguments[2]; + const byteNumber = (parseInt(name[name.length - 2] + name[name.length - 1]) || parseInt(name[name.length - 1])) / 8; + let binArray; + + // Random access + this.getUint8(0); + if (byteNumber === 4) { + binArray = packF32(value); + } else { + binArray = packF64(value); + } + for (let i = 0; i < binArray.length; i++) { + if (endian) { + this.setUint8(originalIdx + byteNumber - i - 1, binArray[i]); + } else { + this.setUint8(originalIdx + i, binArray[i]); + } + } + return undefined; + } +} + +function getBigIntDecorator(wrapped, doMapping) { + return function () { + if (!doMapping){ + this.getUint8(0); + return wrapped.apply(this, arguments); + } + const originalIdx = arguments[0]; + if (originalIdx === undefined) { + wrapped.apply(this, arguments) + } + const endian = arguments[1]; + let hex = []; + let binArray = []; + for (let i = 0; i < 8; i++) { + binArray[binArray.length] = this.getUint8(originalIdx + i); + } + if (endian) { + binArray = binArray.reverse(); + } + for (let i of binArray) { + let h = i.toString(16); + if (h.length % 2) { + h = '0' + h; + } + hex.push(h); + } + let result = BigInt('0x' + hex.join('')); + if (binArray[0] >= 128) { + return result - 18446744073709551615n - 1n; + } + return result + } +} + +function setBigIntDecorator(wrapped, doMapping) { + return function () { + if (!doMapping){ + this.getUint8(0); + return wrapped.apply(this, arguments); + } + const originalIdx = arguments[0]; + let value = arguments[1]; + if (originalIdx === undefined || value === undefined || typeof value !== 'bigint') { + return wrapped.apply(this, arguments) + } + const endian = arguments[2]; + if (value < 0n) { + value = 18446744073709551615n + value + 1n; + } + let hex = BigInt(value).toString(16); + if (hex.length % 2) { + hex = '0' + hex; + } + + const len = hex.length / 2; + let binArray = []; + let j = 0; + // Random access + this.getUint8(0); + for (let i = 0; i < 8; i++) { + if (i < 8 - len) { + binArray[binArray.length] = 0; + } else { + binArray[binArray.length] = parseInt(hex.slice(j, j + 2), 16); + j += 2; + } + } + if (endian) { + binArray.reverse(); + } + for (let i in binArray) { + this.setUint8(originalIdx + parseInt(i), binArray[i]) + } + return undefined; + } +} + +function redefineDataViewFunctions(target, offsetF, doMapping) { + // Replace functions working with Ints + var dataViewTypes = ['getInt8', 'getInt16', 'getInt32', 'getUint8', 'getUint16', 'getUint32']; + for (type of dataViewTypes) { + target[type] = getByteDecorator(target[type], offsetF, type, doMapping); + type = 's' + type.substr(1); + target[type] = setByteDecorator(target[type], offsetF, type, doMapping); + } + + var dataViewTypes2 = ['getFloat32', 'getFloat64']; + for (type of dataViewTypes2) { + target[type] = getFloatDecorator(target[type], type, doMapping); + type = 's' + type.substr(1); + target[type] = setFloatDecorator(target[type], type, doMapping); + } + var dataViewTypes3 = ['getBigInt64', 'getBigUint64']; + for (type of dataViewTypes3) { + target[type] = getBigIntDecorator(target[type], doMapping); + type = 's' + type.substr(1); + target[type] = setBigIntDecorator(target[type], doMapping); + } + +}; + +(function () { + var common_function_body = ` + let _data; + if (typeof target === 'object' && target !== null) { + if (is_proxy in target){ + // If already Proxied array is passed as arg return it + return target; + } + } + _data = new originalF(...arguments); + // No offset + var offsetF = function(x) { + return x; + }; + if (doMapping) { + // Random numbers for offset function + let n = _data.length; + let a; + while (true){ + a = Math.floor(Math.random() * 4096); + if (gcd(a,n) === 1){ + break; + } + } + let b = Math.floor(Math.random() * 4096); + // Define function to calculate offset; + offsetF = function(x) { + if (x === undefined){ + return x; + } + x = x < 0 ? n + x : x; + return (a*x + b) % n ; + }; + let arr = [] + for (let i = 0; i < _data.length; i++) { + arr[i] = _data[i]; + } + + for (let i = 0; i < _data.length; i++) { + _data[offsetF(i)] = arr[i]; + } + } + var proxy = new newProxy(_data, ${proxyHandler}); + // Proxy has to support all methods, original object supports. + ${offsetDecorator}; + ${redefineNewArrayFunctions}; + if (doMapping) { + // Methods have to work with offsets; + redefineNewArrayFunctions(proxy, offsetF); + } + // Preload array + let j; + for (let i = 0; i < _data['length']; i++) { + j = _data[i]; + } + return proxy; + `; + + var wrappers = [ + { + parent_object: 'window', + parent_object_property: 'DataView', + original_function: 'window.DataView', + wrapped_objects: [], + wrapping_function_args: 'buffer, byteOffset, byteLength', + helping_code: packIEEE754 + unpackIEEE754 + packF32 + unpackF32 + packF64 + unpackF64 + ` + function gcd(x, y) { + while(y) { + var t = y; + y = x % y; + x = t; + } + return x; + } + var doMapping = args[0]; + `, + wrapping_function_body: ` + let _data = new originalF(...arguments); + let n = _data.byteLength; + let a; + while (true){ + a = Math.floor(Math.random() * 4096); + if (gcd(a,n) === 1){ + break; + } + } + let b = Math.floor(Math.random() * 4096); + // Define function to calculate offset; + offsetF = function(x) { + if (x === undefined){ + return x; + } + x = x < 0 ? n + x : x; + return (a*x + b) % n ; + }; + ${getByteDecorator} + ${setByteDecorator} + ${getFloatDecorator} + ${setFloatDecorator} + ${getBigIntDecorator} + ${setBigIntDecorator} + ${redefineDataViewFunctions} + for (let i = 0; i < n; i++) { + let random = _data.getUint8(i); + } + if (!doMapping){ + offsetF = function(x) { + return x; + } + } + redefineDataViewFunctions(_data, offsetF, doMapping); + return _data; + `, + }, + ]; + + + let DEFAULT_TYPED_ARRAY_WRAPPER = { + parent_object: 'window', + parent_object_property: '_PROPERTY_', + original_function: 'window._PROPERTY_', + wrapped_objects: [], + helping_code:` + let doMapping = args[0]; + var proxyHandler = ${proxyHandler}; + function gcd(x, y) { + while(y) { + var t = y; + y = x % y; + x = t; + } + return x; + } + + const is_proxy = Symbol('is_proxy'); + const originalProxy = Proxy; + var proxyHandler = { + has (target, key) { + return (is_proxy === key) || (key in target); + } + }; + let newProxy = new originalProxy(originalProxy, { + construct(target, args) { + return new originalProxy(new target(...args), proxyHandler); + } + }); + `, + wrapping_function_args: `target`, + wrapping_function_body: common_function_body, + post_replacement_code: ` + ${constructDecorator} + ${redefineNewArrayConstructors} + redefineNewArrayConstructors(window._PROPERTY_); + ` + }; + + var typedTypes = ['Uint8Array', 'Int8Array', 'Uint8ClampedArray', 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array', 'Float32Array', 'Float64Array', 'BigInt64Array', 'BigUint64Array']; + for (let p of typedTypes) { + let wrapper = {...DEFAULT_TYPED_ARRAY_WRAPPER}; + wrapper.parent_object_property = wrapper.parent_object_property.replace('_PROPERTY_', p); + wrapper.original_function = wrapper.original_function.replace('_PROPERTY_', p); + wrapper.post_replacement_code = wrapper.post_replacement_code.split('_PROPERTY_').join(p); + wrapper.wrapping_function_body += `// ${p};`; + wrappers.push(wrapper); + } + + add_wrappers(wrappers); +})(); -- cgit v1.2.3