0

I am writing my first extension, mainly for fun as I've been in hospital and currently off work so I have been playing about on a new laptop, using a VPN and user-agent switchers that either don't work at all (Opera), or they change the Request Header to the agent BUT not the navigator object, or vice versa.

So I knocked a basic extension up that does both using articles I found on here, with some changes. Such as ensuring the script that overwrites the navigator object is the 1st script run on a page and not as an example I saw just appended it to the document at the end of the DOM, as if a page (such as my own test - see image), references the navigator object before that code is run then there is no point overwriting the object.

I used a webRequest example I saw on here as well to handle the request headers so that both the navigator object and the user-agent header are the same value, I am on Brave but have set the UA to a version of FireFox not out yet e.g version 97.0. The only thing I am having issues with, at the moment, is being able to set the user-agent in one place, I am guessing it needs to be in the background.js as the headers run first, and then passing the data to the content.js so I am not having to hardcode the useragent in 2 files as I am doing currently.

I will look into an interface for the script when I can to make settings easier but I need to know how to pass the data/variables from the background file to the content file.

I have read quite a lot of the articles that came up as I typed my question but a lot of the links people said to read were dead, 404s, I guess due to the questions being 8+ years old, or irrelevant e.g talking about getting data from a page and storing it or passing data from the content.js to the background.js not the other way round. If there is an example page I should be looking at please let me know.

Here are the files so far

Manifest.js

 {
  "manifest_version": 2,
  "description": "Strictly-Software UserAgent Switcher Extension",
  "name": "Strictly-Software UserAgent Switcher",
  "version": "0.2.2022",
  "permissions": ["webRequest", "webRequestBlocking", "<all_urls>"],
  "background": {
    "scripts": ["background.js"]
  },
  "icons": {
    "16": "icon16.png",
    "48": "icon48.png",
    "128": "icon128.png"
  },
  "content_scripts": [
    {
      "matches": [
        "<all_urls>"
      ],      
      "js": ["content.js"]
    }
  ],
  "browser_action": {
    "default_icon": "icon.png"
  }
}

Background.js

chrome.webRequest.onBeforeSendHeaders.addListener(
    function(info) {
        // Replace the User-Agent header
        var headers = info.requestHeaders;
        headers.forEach(function(header, i) {           
            if (header.name.toLowerCase() == 'user-agent') {                
                header.value = "Mozilla/5.0 (Windows NT 10.0; Win32; x86; rv:95.0) Gecko/20100101 Firefox/97.0";
            }
        });  
        return {requestHeaders: headers};
    },
    // Request filter
    {    
        urls: ["<all_urls>"],       
        // In the main window and frames - not working on Browser UA testers that use Iframes e.g https://whichbrowser.net/ or https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_nav_all
        types: ["main_frame", "sub_frame", "other"]
    },
    ["blocking", "requestHeaders"]
);

Content.js

// https://www.iplocation.net/ > uses the headers to work out the browser and agent and I can fool it ok
// my own page uses both navigator and headers and works ok
// https://whichbrowser.net/ > shows my fake useragent below a more accurate test as I am using Brave but pretending to be a version of FireFox not out yet e.g v 97.0
// https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_nav_all > doesnt work as uses an iframe how do I get my code to work on that frame?
var actualCode =  '(' + function() {
    'use strict';
    //console.log("running useragent script insert extentsion");
    var navigator = window.navigator;
    var useragent = "Mozilla/5.0 (Windows NT 10.0; Win32; x86; rv:95.0) Gecko/20100101 Firefox/97.0";
    var platform = "Win32";
    var appversion = useragent.replace(/^\w+[^/]+?\//,"");
    var appname = "FireFox";
    var modifiedNavigator;
    if ('userAgent' in Navigator.prototype) {
        // Chrome 43+ moved all properties from navigator to the prototype,
        // so we have to modify the prototype instead of navigator.
        modifiedNavigator = Navigator.prototype;
        //console.log("Chrome so set modifiedNavigator to Navigator.prototype");
    } else {
        //console.log("Not Chrome so create new Navigator object");
        // Chrome 42- defined the property on navigator.
        modifiedNavigator = Object.create(navigator);
        Object.defineProperty(window, 'navigator', {
            value: modifiedNavigator,
            configurable: false,
            enumerable: false,
            writable: false
        });
    }
    // Pretend to be Windows XP
    Object.defineProperties(modifiedNavigator, {
        userAgent: {
            value: useragent,
            configurable: false,
            enumerable: true,
            writable: false
        },
        appVersion: {
            value: appversion,
            configurable: false,
            enumerable: true,
            writable: false
        },
        appName: {
            value: appname,
            configurable: false,
            enumerable: true,
            writable: false
        },
        platform: {
            value: platform,
            configurable: false,
            enumerable: true,
            writable: false
        },
    });
} + ')();';

// build script element up
var script = document.createElement('script');
script.type = 'text/javascript';
script.textContent = actualCode;

// we want our script to run 1st incase on the page another script references navigator before we can overwrite it
if(typeof(document.head) !== undefined && document.head !== null)
{   
    // check page has a head as it might be old n crusty html 
    let head = document.head;
    let scriptone = document.head.getElementsByTagName('script')[0];

    // if a script exists then insert it before 1st one so we dont have code referencing navigator before we can overwrite it
    // if no script exists then it will be inserted at the end of heads child nodes
    if(typeof(scriptone) !== undefined && scriptone !== null){      
        head.insertBefore(script, scriptone);
    }
}else{
    // fallback for old HTML just append at end of document and hope no navigator reference is made before this runs
    document.documentElement.appendChild(script);
}
// remove script
// script.remove();

I have seen references to messaging but some of the example urls people say to check are dead, or they are talking about variables going from content to background not background to content. As I am assuming with the headers being changed the background.js file runs 1st before needing to pass info to the content file where the navigator object is over written. It might be the case, as most of the questions were 8+ yrs old that I might be needing to use totally different code or methods of passing data that was around in those days?

I am using a useragent that doesn't exist FireFox 97 on Brave so that I can test easily that the UA is changed. The only other problem is that on pages like https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_nav_all which use iframes, I cannot get the code to work. I thought adding the frames into the manifest would enable that but I am guessing they meant old school frames not iframes or that some other method is required to get values injected into the frame document.

This is my own test page I use, mainly because I am testing some VPNS and useragent switcher extensions, so it just shows me where I am, the ISP, the IPv4/v6 addresses and the useragent from both headers and navigator.

I have edited the IP addresses and location as I fondly remember 25yrs ago posting a forum question at my 2nd job which included the IP address of our Server, with the SA login (which was used back then) and then the uproar as we suddenly got rushed with hack attempts. So I thought it best to just edit the code.

I know using useragents for feature detection is not the way to go, feature detection is always better than trying to work out a browser before branching, however I have noticed when using user-agent switchers that many sites still must be branching their code from the UA, as if I just change the header UA to an iPhone or iPad or KitKat or Safari on Mac, the page layout changes, looks awful etc etc so I guess a lot of coders still don't understand why feature detection is better than browser detection for some reason.

Anyway this page is just for myself when using a VPN and a bit of fun as I am off sick so I wanted to learn something I hadn't done before and I thought an extension for Brave/Chrome/Edge would be interesting to code.

My Browser User-Agent test page for Navigator and Request headers

A simple link to a page that explains what I need to do might be all I need.

halfer
  • 19,824
  • 17
  • 99
  • 186
MonkeyMagix
  • 677
  • 2
  • 10
  • 30
  • Add `all_frames`, `match_about_blank`, and `run_at` of `document_start`, [more info](https://developer.chrome.com/extensions/content_scripts). – wOxxOm Jan 10 '22 at 22:11
  • will just fix the iframe issue but what about sending messages/data e.g the useragent I want to spoof from the background.js file to the content.js file? – MonkeyMagix Jan 11 '22 at 01:03
  • Thanks, that fixed the issue with the iframe on https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_nav_all so my UA is injected into the iframe. Will have to study that document some more to find out about passing info from the background file to content file. – MonkeyMagix Jan 11 '22 at 01:21
  • See [Injecting javascript variable before content script](https://stackoverflow.com/q/45102497) and study how it's implemented in other extensions e.g. Stylus, Violentmonkey. – wOxxOm Jan 11 '22 at 09:41
  • Ok will do thanks for the heads up on where to look. By the way do you know of any GOOD, articles on writing your 1st Google Extension with a front end that would be good to read rather than repository of methods and properties e,g someones blog who wrote a "write your first extension" article. Also this same code works on Chrome,Edge and Brave, how hard would it be to change to Mozilla, FireFox, do they use totally different methods, properties or is it as easy as changing the method names from chrome.xxto mozilla.xxetc. Is there a "standard" for extensions or is it browser engine specific? – MonkeyMagix Jan 12 '22 at 18:33

0 Answers0