TZP: trap resource:// blocking

I make sure several global vars are set before we start fingerprinting: such as isFF, isOS, isTB, isEngine, prototype lies. Unfortunately, isTB never resolves with cydec (at least in total mode), and thus the promise never happens and we have no output: section re-runs work sweet 😃

note: isTB is initially an empty string = ā€œā€, and I set it once to a boolean. I do not have any instances of (!isFF). TB shouldn’t have any extensions that cause the script to break, so I’m happy to trap this and change isTB into false based on assumptions

note: this is the only resource:// that is set via code. The only other one is an image set via css: and that function works on the same assumption: if it’s there = TB, if it isn’t = not TB.

here is the code

function get_isTB() {
	return new Promise(resolve => {
		if (isFF && isTB === "") {
			try {
				let t0 = performance.now()
				console.debug("a")
				let css = document.createElement("link")
				console.debug("b")
				css.href = "resource://torbutton-assets/aboutTor.css"
				css.type = "text/css"
				css.rel = "stylesheet"
				console.debug("c")
				document.head.appendChild(css)
				console.debug("d")
				css.onload = function() {
					isTB = true
					debug_page("TB","     resource:// = aboutTor.css")
					if (logPerf) {debug_perf("[yes] isTB [immutables]",t0)}
					return resolve("done")
				}
				css.onerror = function() {
					isTB = false
					if (logPerf) {debug_perf("[no] isTB [immutables]",t0)}
					return resolve("done")
				}
				document.head.removeChild(css)
			} catch(e) {
				console.error("get_isTB", e.name, e.message)
				return resolve("error")
			}
		} else {
			return resolve("done")
		}
	})
}

everything debugs - a,b,c,d, no error is trapped. So I need a timeout

These three run a single Promise.all -> prototypeLies promise -> kick it all off

Firefox

perf detail: global
             isOS [immutables]:    0 ms |    0 ms
         isEngine [immutables]:    0 ms |    1 ms
        [no] isTB [immutables]:    1 ms |    2 ms

TB with RFP disabled so I can get some perf

perf detail: global
             isOS [immutables]:    1 ms |    1 ms
         isEngine [immutables]:    1 ms |    2 ms
       [yes] isTB [immutables]:   17 ms |   19 ms

So I’m happy to add a timeout, but not sure of the syntax to add that. That is e.g. after 100ms set isTB true and resolve as rejected or something - help appreciated

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 1
  • Comments: 18 (11 by maintainers)

Most upvoted comments

OK, I’ll rerun prototype for section reruns as well 😃

I notice the delay (I use F5) on file:// … have to close and restart nightly after a dozen or of them. Not noticed it on http … so will be interesting when I upload all my changes: so much stuff here

I changed up line-height and scrollbar info by only reporting a narrower range of known zoom values: since FF recently added 400 and 500% - that and I think a lot of values on Linux will change with native theming disabled. In order to test it I made it update-able in real time: that was fun.

big-n-small

closing: have sorted out timing out in promises, and overall hardened all the isThings used throughout my code: promising them once on each pageload

@sereneblue FYI: on Cydec

  • cydec also tries to mess with error strings (which is not a real solution IMO, since what errors are generated and how they are, can be different depending on the script: i.e it’s a game of whack a mole). The error’s generated in my test are from passing a string to a new function (thanks to abraham)
const newFn = x => typeof x != 'string' ? x : new Function(x)()

//and here are the three error strings used to check if you are firefox
try {newFn("alert('A)")} catch(e) {res.push(e.name +": "+ e.message)}
try {newFn(`null.value = 1`)} catch(e) {res.push(e.name +": "+ e.message)}
try {let test = newFn("let a = 1_00_;")} catch(e) {res.push(e.name +": "+ e.message)}
  • cydec also mangles math function results: which is the fourth isFF test (the one which also sets the isEngine)

That cydec really is a piece of work: right now I’m returning [object Object] in navigator.connection because of it’s hijinks

Correct. Here’s an example of Privacy Possom starting with none on page load and then 10 lies on re-run.

possom2

Yes, that’s it. The timeout resolve only resolves the promise if the promise is still pending.


If you like, you can reduce some of the nested bracket layers by checking if !(isFF && isTB === ""), resolve early if true, and then the else block can be omitted since a false condition will execute everything else. It reads fine either way though.

function get_isTB() {
    return new Promise(resolve => {
        // resolve early if condition is true
        if (!(isFF && isTB === "")) {
            return resolve("done")
        }
        // else
        try {

        } catch (e) {

        }
    })
}

…And, an arrow function can remove the nested function brackets (minimal nesting).

const get_isTB = () => new Promise(resolve => {
    // resolve early if condition is true
    if (!(isFF && isTB === "")) {
        return resolve("done")
    }
    // else
    try {

    } catch (e) {

    }
})

you mean like this? I think I get it now, the resolve can only be resolved once, then the promise is over, including the timeout?

function get_isTB() {
	return new Promise(resolve => {
		if (isFF && isTB === "") {
			try {
				// extensions can cause this to never promise
					// FF is ~1ms, TB is ~20ms
				var timeout = window.setTimeout(function(){
					// assume false: if actually TB, you're a CRAZY edge case
					isTB = false
					console.debug("get_isTB timed out")
					resolve("get_isTB timed out")
				}, 100)
				//setTimeout(() => resolve("promise failed"), 100)

				let t0 = performance.now()
				// etc