1. 19
  1.  

  2. 9

    You could do Function.prototype.call(f), but if you’re worried about code running in your own context changing things like that, then you have to accept them changing anything else that you can call or compare against, including APIs that you could use to create frames.

    What you’re trying to do is fundamentally “I want to do something trustworthy in a compromised environment”, which is an already lost battle.

    1. 4

      One fix for this behavior is of course something along the lines of:

      Function.prototype.toString = function() { return "[native code]"; }
      

      (/s) I honestly kind of love how malleable javascript is. With a few more rules to it, it would be like one of those programming puzzle games.

      1. 3

        What if you create an iframe and then have the iframe send back the clean function to test against? I guess malicious code could trap iframe creation, but it would be pretty complicated. Any better techniques from the battle against spam ad clicks?

        1. 3

          Even if you could send natives across that boundary, they’re not the same object. Each frame has its own globals, so monkey-patching String in one doesn’t mess with the others.

          1. 3

            I assumed they were saying “get the Function.prototype.toString function from the other frame”, as that’s the only thing that makes sense because of what you just said :)

          2. 3

            Or just run the code you don’t want patched in an iframe with no 3rd-party scripts and have a stub in the parent using something like Comlink to expose it.

            1. 1

              True. But that wouldn’t work for APIs that interact with the document (such as document.createElement).

              1. 2

                Well, there’s some truly bizarre stuff out there like https://partytown.builder.io that runs a service worker that intercepts certain pseudo-URLs and then uses non-async XHR calls to send non-async third party scripts off asynchronously into a service worker. But yeah, probably it has some limit somewhere.

                1. 1

                  Yup, but even with Partytown or with atomics you would have to access the main thread’s DOM API. I get your point though.

                2. 1

                  If you don’t want elements to be tampered with, they need to be in an iframe anyway.

              2. 1

                You wouldn’t be able to compare the function in the in the iframe with the one outside of it because you’d need to serialise it to pass it around.

                1. 4

                  You can pass functions between frames in the same origin. This snippet creates a new iframe which passes its window.fetch to the parent:

                  window.gotFetch = (f) => { console.log('got fetch from child:', f);};
                  const iframe = document.createElement('iframe');
                  document.body.appendChild(iframe);
                  iframe.contentDocument.write('<script>')
                  iframe.contentDocument.write('console.log("child fetch", window.fetch);');
                  iframe.contentDocument.write('window.parent.gotFetch(window.fetch);');
                  iframe.contentDocument.write('</script>');
                  
                  1. 2

                    You’re right, I didn’t explain it well enough. To add some more context:

                    • Cross-origin, you’ll have the serialization issue.
                    • On same-origin, you can can pass references around, but comparing them (with a referential equality check) would still return a falsy value because they’re pointing at two different functions — but feel free to correct me if I’m missing something.

                    I can’t run it now, but what I mean is: in your example, in the “gotFetch” callback, a “f === window.fetch” statement would return “false”, right?

                    1. 4

                      On firefox at least, (f === window.fetch) === false. Though you could use this trick to get access to an unaltered version of a function, which is pretty neat.

                      1. 4

                        I was gonna say that you could still not fully trust that function because third-parties could still override the iframing API, and that sometimes there might be limitations in using iframes (e.g. CSP)… But you know what? When the goal is “using” a clean native function and not just checking if it’s monkey patched, in most cases this solution does seem more practical than the one I mentioned in the article. I’ll update it, thank you all for the discussion.

                        1. 1

                          Before I forget, I just noticed that another limitation of this approach is that you won’t be able to use it for APIs that interact with the document (such as document.createElement).

                          1. 2

                            Yeah, I thought that after your comment.