Proxomitron and Javascript

My Tame Javascript filter is getting so complicated I feel it deserves its own page. Here I've described a general strategy for using Javascript to your own advantage. I explain the tricks I used in my start.js and end.js files, both of which are heavily commented if you'd rather just look at those. (Important: Make sure you bypass Proxomitron when downloading these two files.) If you're unfamiliar with specific aspects of the Javascript language, this DevGuru site is as good a reference as any.

You'll quickly notice that I'm no fan of Javascript or any other scripting language on the web. The best setting for these features from a security, privacy, or sanity standpoint is, and will always be, "off" unless a site needs them to function. Even then, it's better to just find another site if possible. For those times when you absolutely must run Javascript, this technique should keep you protected from most of its stupidity.


Neutralizing built-in Javascript APIs

There are many functions available to Javascript authors that, quite frankly, shouldn't be. Scripts can make windows go in and out of focus, resize themselves, or bounce around the screen. (Warning: The linked page will attempt to trap you with a document.onunload handler, so be careful.) Another example is the infamous pop-under ads from x10.com. These nuisances often arise from webmasters who thoughtlessly assume that theirs is the only page you're reading and that your web browser is the only program running on your desktop.

If I load a page in the background, I may be reading another while I wait. But many authors use something like <body onload="window.focus();"> to force their page to the front when it's done loading. Gee, thanks for nothing, stupid webmaster; and by the way, if your page wasn't bloated with 157 images I wouldn't have had to load it in the background in the first place. The following Javascript functions have absolutely no legitimate use: resizeTo, resizeBy, moveTo, moveBy, scrollTo, scrollBy, blur, focus. Others like setTimeout and setInterval are questionable at best. Most often they're used for status bar scrollers and other continuously running distractions.

So what's the way to deal with this? Well until browsers themselves provide ways for the user to override or ignore these functions, we'll have to resort to Proxomitron. We create a filter to call start.js at the beginning of each HTML file, before any author-written script can have a chance to run. In this file, let's redefine each of these to do nothing at all:

function resizeTo(w, h)  { return true; }

This doesn't always work though, and there seems to be no pattern to when it works and when it doesn't as far as I can tell. I played with this some more and came up with this alternative, which seems more reliable:

resizeTo = new Function('return true;');

Don't ask me what the difference is. A further wrinkle: The second is invalid in Internet Explorer, but the first seems to work reliably there. Another consideration is that these functions can also be invoked with an object, e.g. window.resizeTo(0, 0). Sometimes the override works in this case and sometimes it doesn't, it seems to depend on the browser and the time of day for all I can tell. It helps, however, if we also do this:

window.resizeTo = resizeTo;

and just for the hell of it:

window.parent.resizeTo = resizeTo;
window.top.resizeTo = resizeTo;
this.resizeTo = resizeTo;
self.resizeTo = resizeTo;

With this change, it's good enough to stop this silly MP3 site from resizing itself, which it didn't do before.

Pop-up windows

To prevent pop-up windows, we could apply the approach in the previous section to window.open, but that turns out to be too heavy-handed to be practical. A lot of authors write links like <a href="javascript:window.open('blah.html')"> that would be rendered non-functional if we did this. Instead, we'll replace open with our own openPROX that does its own processing and maybe calls the real open.

Unfortunately the trick doesn't completely work in my version of Internet Explorer so I had to do something a bit less complicated in that case. It's still good enough for 99% of web pages out there, but those few that do window.open('') to open a blank window and then document.write to it will fail.

The open function I've provided does a number of things, any of which you can modify to your heart's content:

  1. If the page is not yet fully loaded, it does nothing and returns null.
  2. If the domain of the target URL is different than that of the current URL (which is the case for almost all ad pop-ups), also return null.
  3. If the target is a new window, use the current window instead. This transforms those annoying Javascript-new-window links into normal, back-button-friendly links.
  4. Or, if you prefer a new window, it makes sure it has full controls (scrollbars, address, etc.) on it and makes it a reasonable size. Some authors like to hide pop-ups offscreen or size them to 1x1 pixels.

What makes this difficult is that if we simply redefine open, there will be no way for the new version to call the old, and any attempt to do so will result in infinite recursion. At first I tried simply stashing the old value of open before defining the new one:

var oldOpenPROX = open;

function open(url, name, props)
{
  if (... some condition ...)
    return oldOpenPROX(url, name, props);
  else
    return null;
}
window.open = open;

This was unreliable. Sometimes oldOpenPROX would have the native version of open and other times it would mysteriously end up pointing to the redefined open instead. This of course is very bad because then open calls itself recursively forever.

The next attempt fared better:

function openPROX(url, name, props)
{
  if (... some condition ...)
    return oldOpenPROX(url, name, props);
  else
    return null;
}
var oldOpenPROX = open;
open = new Function('u', 'n', 'p', 'return openPROX(u, n, p);');
window.open = openPROX;

Explicitly creating a new Function object with the constructor somehow gets around this "reference" problem. Of course, that doesn't work in IE because it treats built-in functions like open as read-only, but luckily this works instead:

if (isInternetExplorerPROX)
  eval('function open(url, name, props)  { return openPROX(url, name, props); }');
else
  open = new Function('u', 'n', 'p', 'return openPROX(u, n, p);');

However this still doesn't solve the reference problem in IE. Arrgh! Stupid ill-defined language! I finally gave up and modified openPROX so that it never attempts to call the native open function in Internet Explorer. In a moment of temporary insanity, I downloaded IE6 to see if it handles this any better. Nope.

Link targets

One thing that's difficult to do with a conventional filter is preventing links from opening in new windows with the target attribute. Catching obvious ones like target="_blank" is easy enough, but a new window will also be opened if the target is any other string than that of an existing frame. There's no way to know at Proxomitron's level if target="content" refers to a new window or to a valid frame in the current window.

Javascript solves this dilemma. In end.js there's a loop over all links on the page that checks their targets against the names of all the frames in the current window. Any that don't match are reset to something more desirable. The only minor drawback is that if you use a link before the page is fully loaded and end.js has had a chance to execute, it will use its original target.

Preventing scripts from acting on UI events

Another "feature" of Javascript is the ability to attach any piece of code to browser events, such as a page loading or a mouse click. While there might be three or four legitimate uses of this out of all the millions of obvious ways to abuse it, I'm not particularly impressed with the creators of Javascript here. A little common sense could have gone a long way to prevent pages from trivially crashing computers with hundreds of unsolicited pop-ups. (Warning: The linked page will bury you in copies of itself unless you have protection against onload and onunload.)

In end.js I added code to loop through objects and set various event handlers to null. This is more precise than searching for, say, onload in the source code. I've also done away with all rollover images and links using this method. If you don't like having to download two images for every img tag just to have some cheesy light-up-on-mouseover effect, you may appreciate this, but it might exacerbate the effects of Mystery Meat Navigation.

This can also be used to stop anti cut-and-paste scripts. Those that work by popping up an alert box whenever you click on the page can be stopped by setting the page's onclick and onmousedown handlers to null. There's no harm in setting a property that isn't used, so rather than fuss over which objects can have which event handlers defined, end.js simply sets every known event handler to null on document, window, and several other variants.

Privacy enhancements

The Fake referrer info filter takes care of the HTTP Referer header, but you might also want to set the Javascript referrer similarly. In start.js, I set document.referrer to http://currenthost/. I also set all screen measurement properties to random numbers for the fun of it. Not too useful, but I'd like to see the look on some webmaster's face when visitors with a 3440.3515732291635 by 2215.6437879573964 screen and 2^7124.546037171544 colors per pixel start showing up in his logs. Internet Explorer doesn't seem to like this--since these properties are supposed to be read-only, I guess that's understandable--so I've disabled that code for IE.

A more serious application is the Javascript history object, which theoretically exposes your entire browsing history for that window. In my tests though, I couldn't extract the actual URLs from the history, only the number of elements. Did I do something wrong, or did Javascript designers actually show some restraint for once? Either way, there's no harm in setting its length to 0, which will make it look like you came there in a fresh window.

Other gadgets

Not only can you write scripts to combat PAS (Page Author Stupidity/Stupid), you can also use it to add your own widgets to any page. Inspired by a poster on the GRC discussion forum grc.techtalk.localproxies, I added a simple function to translate the current page from a given language. It's nowhere near as fancy as others, but it serves as a good example. Create a bookmarklet with the address javascript:translatePagePROX(). When you invoke the bookmark, you will be prompted for the language you wish to translate from. Use the standard two-letter language codes (de for German, fr for French, etc.) to redirect the current URL through translate.google.com. If you a want target language other than English, or a translating site other than Google, modify the translatePagePROX function as needed.

Anything available to the author is now at your disposal through start.js and end.js. Now you always get the first and last word on what a web page does to your computer. You can simply disarm harmful scripts or add your own helpful tools such as this experimental DOM-editor to every page.

Test page

I created a quick-and-dirty test page to make sure that the overrides are working properly. If you feel confident in your defenses, try it out. The worst it will do is open a couple easily disposed-of windows to a non-existent site.

This is a cached copy of http://www.geocities.com/u82011729/prox/js.html