skip to Main Content

I am working on a Chrome extension, and it’s currently published with Manifest v2,
I also have a Manifest v3 version, however there’s one small difference in functionality.

Consider the following content script, same for v2 and v3:

var AGScript = `

    var setAgent = function(win)
    {
        try
        {
            Object.defineProperty(win.navigator, "userAgentData", {value:undefined});
            Object.defineProperty(win.navigator, "platform", {value:undefined});
            Object.defineProperty(win.navigator, "vendor", {value:undefined});
            Object.defineProperty(win.navigator, "userAgent", {value:"xxx"});
            Object.defineProperty(win.navigator, "appVersion", {value:"xxx"});
        } catch(e) {}
    };

    document.addEventListener("DOMContentLoaded", function(event) { setAgent(window); });
    document.addEventListener("DOMNodeInserted", function(event) { if (event.target.tagName == "IFRAME") setAgent(event.target.contentWindow); });
    setAgent(window);

`;

var script = document.createElement("script");
script.type = "text/javascript";
script.textContent = AGScript;
document.documentElement.appendChild(script);
script.remove();

And the following v2 manifest section:

   "content_scripts": [ {
      "all_frames": true,
      "js": [ "content-ag.js" ],
      "match_about_blank": true,
      "matches": [ "http://*/*", "https://*/*", "file:///*" ],
      "run_at": "document_start"
   } ]

Also, a v3 manifest:

   "content_scripts": [ {
      "all_frames": true,
      "js": [ "content-ag.js" ],
      "matches": [ "<all_urls>" ],
      "run_at": "document_start",
      "world": "MAIN"
   } ]

When testing on the following page: https://browserleaks.com/javascript
the v2 redefines userAgent for both window and iframe.contentWindow,
and yet v3 is only able to do it in window.

I have tried playing with different manifest options, but just can’t get it.

Yet, on the following page: https://webbrowsertools.com/useragent
both versions are able to change everything, including iframes.

This leads me to believe it’s a timing problem.


Update.

As suggested I have added "match_origin_as_fallback": true, as well as changed the content script to the following:

(() => {

        console.log("here!");

        var setAgent = function(win)
        {
            try
            {
                Object.defineProperty(win.navigator, "userAgentData", {value:undefined});
                Object.defineProperty(win.navigator, "platform", {value:undefined});
                Object.defineProperty(win.navigator, "vendor", {value:undefined});
                Object.defineProperty(win.navigator, "userAgent", {value:"xxx"});
                Object.defineProperty(win.navigator, "appVersion", {value:"xxx"});
            } catch(e) {}
        };

        document.addEventListener("DOMContentLoaded", function(event) { setAgent(window); });
        document.addEventListener("DOMNodeInserted", function(event) { if (event.target.tagName == "IFRAME") setAgent(event.target.contentWindow); });
        setAgent(window);

 })()

Unfortunately userAgent is not overridden when tested with https://webbrowsertools.com/useragent/

here! is printed in the console. Did I mess up somewhere?
(I was trying in manifest v2, not that it should make any difference)

2

Answers


  1. What a bizarre way of adding script to one’s own document! 🙂

    When you could’ve just…

    <script>
    
    function setAgent(win){
    try {
     Object.defineProperty(win.navigator, "userAgentData", {value:undefined});
     Object.defineProperty(win.navigator, "platform", {value:undefined});
     Object.defineProperty(win.navigator, "vendor", {value:undefined});
     Object.defineProperty(win.navigator, "userAgent", {value:"xxx"});
     Object.defineProperty(win.navigator, "appVersion", {value:"xxx"});
    
    } catch(e) {}}
    
    document.addEventListener("DOMContentLoaded", function(event) { setAgent(window); });
    document.addEventListener("DOMNodeInserted", function(event) { if (event.target.tagName == "IFRAME") setAgent(event.target.contentWindow); });
    setAgent(window);
    
    </script>
    

    …and saved yourself a ton of typing and even unnecessary delays!

    Ah well… I guess some like to play the mysterious! 🙂

    Onto the problem at hand…

    Observation 1:

    Instead of "DOMNodeInserted" use "DOMNodeInsertedIntoDocument".

    The former is suitable for fragments while the latter is self-explanatory.

    Observation 2:

    You’re setting the agent up twice on the main window…

    1. document.addEventListener("DOMContentLoaded", function(event) { setAgent(window); });

    2. setAgent(window);

    Why is that? Is that because DOMContentLoaded wasn’t working for you by any chance?

    Observation 3:

    Why were you not using <all_urls> in manifest II when it was possible since 2011? Or thereabouts…

    Login or Signup to reply.
  2. Looks like a bug in Chrome.

    A possible workaround is to intercept the getter for contentWindow:

    (function setAgent(wnd) {
      try {
        const nav = wnd.navigator;
        const {apply} = Reflect;
        const fp = HTMLIFrameElement.prototype;
        const descCW = Object.getOwnPropertyDescriptor(fp, 'contentWindow');
        descCW.get = new Proxy(descCW.get, {
          __proto__: null,
          apply: (fn, thisArg, args) => setAgent(apply(fn, thisArg, args)),
        });
        Object.defineProperty(fp, 'contentWindow', descCW);
        Object.defineProperty(nav, 'userAgentData', {value: undefined});
        Object.defineProperty(nav, 'platform', {value: undefined});
        Object.defineProperty(nav, 'vendor', {value: undefined});
        Object.defineProperty(nav, 'userAgent', {value: 'xxx'});
        Object.defineProperty(nav, 'appVersion', {value: 'xxx'});
      } catch (e) {}
      return wnd;
    })(window);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search