miscoded

the web is a hack

Subscribe to RSS feed

Posts tagged with "coding"

Prototype findOrStore considered harmful

, , ,

Prototype 1.5.0 completely breaks CNN on certain mobile versions of Opera. All the text disappears.

We've dug up the source and it appears to be code from the common prototype.js that causes problems, CNN's prototype.js is here:


Element.extend.cache = {
  findOrStore: function(value) {
    return this[value] = this[value] || function() {
      return value.apply(null, [this].concat($A(arguments)));
    }
  }
};


Problem arises when the "value" argument is a function object. Then this[value] = this[value] requires decompilation, in order to create a string for the property name.

This is a bad idea because

  • Decompilation support is an optional feature. We disable it on many limited platforms for performance
  • Decompiling a function is in any case not a reliable way to create a unique hash key since functions can have exactly the same source code but live in different scopes or different closures, so this code as-is will create obscure bugs that are very hard to track down.


I hope the excellent Prototype devs can find a better approach. Unfortunately this code is still there in the latest version.

(Due to impatient family members I can't bug it in their tracker right now, post a link to the bug in comments if you do.)

DOMStorage and security

, , , ...

The DOMStorage feature of the WHATWG WebApps spec is criticised for the security implications. 0x000000 has excellent points (particularly in comments) and I'll think out loud about how UAs like Firefox and Opera will need to address them. I'm not terribly good at spec writing and not implementing this stuff myself so only personal opinion and thoughts to be found here..

Quoting 0x000000:

Starting in Firefox 2 but mainly in Firefox 3, the browser is fully capable of restoring this sessiondata after a crash. This is interesting because we could on purposely crash a window and inject sessiondata to make it an persistent denial of service



Yes, that's a real threat. Opera already addresses a similar issue in the dialog you get on resuming after a crash, where you can choose to start without open windows. An even better implementation might be to detect what window or session caused a crash, and attempt to restore all others - I assume it would require quite some engineering like a separate monitoring process or something. In any case, users will need a "start without restoring sessions" option to avoid persistent session storage enabling persistent crashers.

this storage engine allows me to store what ever I want, whenever I want, and it allows huge data -if I have access to that page- which is useful when there is some XSS hole on that page



Um, if there is an XSS hole on the page you've got the data anyway, is this really more dangerous than just submitting it to a server you control?

It can be used to store worms and activate them across webpages instead of calling remote scripts



Still requires some XSS vulnerability on the page you want to run the worm on. If white-list restriction is implemented it requires both attacker and target to be white-listed.

it can used to store heapspray shellcode to actively stay under the radar of anti virus programs



The bad code needs to travel over the wire at least once to get to the local storage, so that's not entirely true but still an interesting point. I guess it's a matter of timing. If you can get a piece of malware into the local storage of a sufficient number of UAs before security software is updated to detect this threat, you could then activate the attach with some fairly innocent-looking JS that might be much harder to filter out. Should UA vendors collaborate with Antivirus companies on enabling local storage scanning? I'm not sure here, guess malware detection evasion side effects of local storage need more thinking..

it can store a great deal of information later to used on sub domains to read them out again and use them, like defeating single sign on schemes



I'm not sure what he means by "defeating single sign on schemes" here.

it can also be used to profile users and de-annonymize them by storing data vectors in their browser



Yes, it can if you find an XSS hole on a site I give personal information to (or if a web developer by mistake stores given information in a too public part of the local storage).

Now, security concerns are addressed by the specification. Perhaps it's too easy to overlook, but note how the spec explicitly states that

user agents may prevent domains from storing data in and reading data from the top-level domain entries in the globalStorage object


and I guess UAs will in practice require per-domain white-listing of domains that are allowed to use global storage, or make it available only in "application-like" contexts such as widgets. I can't imagine any UAs implementing this part of the spec without additional security restrictions, that would indeed be crazy. On the other hand, per-domain whitelisting will address most of his potential exploits as long as the white-listed sites themselves are safe.

Nevertheless, the spec gives greater powers to web developers. It will enable them to do interesting things more easily, but as we know with greater powers comes greater responsibility - including responsibility for coding responsibly and preventing XSS. The greater capabilities we give JavaScript, the greater the damage a single XSS exploit can cause.

(Opera doesn't support DOMStorage yet but we probably will at some point, apparently Firefox 3 will support it according to 0x000000's post.)

can you improve ebDCKillCheckMousedown?

, ,

Busy with my regular passtime eBay JavaScript browsing (I think I spend more time on their site reading their .js files than looking at auction listings) I came across this:

var ebAllowClick=true;function ebDCKillCheckMousedown()
{if(ebAllowClick)
{ebAllowClick=false;setTimeout("ebAllowClick=true;",1250);}}
document.onclick=ebDCKillCheckMousedown;


Now, there is nothing technically wrong with that but I'm not terribly impressed with the quality of this snippet. Actually I find it hard to believe that a site with the scale and resources of eBay doesn't do review their scripts more carefully for efficiency.. but rather than me launching into one of my familiar rants I'll just pass the buck this time: so, if you were to teach the author a thing or two about efficient JavaScripting, what would you say?

Edit: proofreading the posted post I noticed yet another bug, not just performance improvements stuff..

A problem with John Resig's addEvent

, , , ...

A while ago, Peter-Paul Koch and some other JavaScript-interested bloggers held a competition for improving the "addEvent" function. The winner was John Resig's flexible JavaScript events function.

It creates a new property of the object you add an event listener to as follows:
obj['e'+type+fn] = fn;

The main purpose of that is to make the "this" keyword work inside event listeners. It is a clever hack, though looks a bit ugly - to ensure that the object property has a unique name, it concatenates "type" and "fn", meaning the browser has to decompile the function and use its entire source as part of the property name. Decompiling a function object is slow, the property name becomes nothing short of ugly. Perhaps a few lines of code to make a prettier unique property name would be worth it?

Besides the ugliness, here comes the real problem: the code assumes that all implementations can decompile functions. Opera can on desktop, but to save footprint this feature is disabled on mobile phones. Function.prototype.toString simply returns "[ECMAScript code]". Hence, each call to Resig's addEvent will overwrite the previous function object for that type, and the last function added will be called as many times as addEvent itself has been called. I'm afraid the clever hack has turned rather destructive - bad enough to break cross-platform compatibility for any app.

As written, the function makes the assumption that all browsers which have attachEvent support has IE's bugs. The simple fix would be to follow this rule: use object detection and try the standards-compliant path first. First looking for the standards-compliant (thus probably the best defined) function might be a good general rule of thumb for compatibility. Cross-browser support for a method that is implemented by trying to figure out what another browser is doing is likely worse than support for a method specified in detail by the W3C, so by choosing the W3C stuff if it exists should get you better compatibility.

how to say hello world with JavaScript

, ,

..if you're Google

My apologies, this isn't funny. This post won't even try to be funny. Promise.

Just a trick question for my skilled readers: where is the code that runs when you click on the button on this page?

HP.com no clue NOSCRIPT

,

HP.com's shopping section has a peculiar understanding of the semantics of the NOSCRIPT element. For each SCRIPT element on the page they include a NOSCRIPT describing in plain English what the SCRIPT element is meant to do. For example

<noscript>This script have functions for quick shop value search and letter to numbers </noscript>
<noscript>This script block populates the dynamic menu bar</noscript>


..so if your UA doesn't support JavaScript you get lots of really useful information about what you are missing. I guess they show this information to make JavaScript-hating visitors understand that they must populate the dynamic menu bar by hand.

interesting jQuery stuff

, , ,

The other day (well, night actually) I was looking at a problem on a site that used the jQuery library. Though I've heard of it, I haven't seen jQuery on live sites before and I thought it shows off some quite interesting features of JavaScript.

The latest version of the library is available so you can see what I'm talking about.

A function can call itself recursively as constructor

Just inside the jQuery function we find:

if ( window == this )
	return new jQuery(a,c);


This clever little trick means you don't need to use the "new" keyword all over your code to create a new jQuery object. You can just do
var obj=jQuery()
and the this check will detect that it wasn't called as a constructor and call itself recursively, this time using the "new" keyword to define the expected jQuery object.

I have a question about the if clause though - it presumably means trouble if you change the "this" object and use something else than window. Say,
var obj = jQuery.call(document)
would probably break something. I don't know jQuery well enough to tell if that would be a problem, but the check could perhaps be
if(this.constructor!=arguments.callee)
to catch all cases?

Using || operator to default to an argument inside a function call

You may be used to seeing the || operator being used to provide a default value for a variable. For example
var username=prompt('Your name please')||'anonymous'
will set the variable to "anonymous" if the user cancels the prompt or doesn't type any value.

It still took me a while to understand what is going on here:

return this.setArray(
	// HANDLE: $(array)
	a.constructor == Array && a ||

	// HANDLE: $(arraylike)
	// Watch for when an array-like object is passed as the selector
	(a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a ) ||

	// HANDLE: $(*)
	[ a ] );


The basic idea is: if the object a is an array, we pass it to the function directly. If it is an object that is somewhat similar to an array (for example a NodeList), we pass the result of calling the jQuery.makeArray function which will take all the elements/nodes and add them to an actual array. Otherwise, we just pass an array containing nothing but the object a.

The key to understanding this is to understand that the statement
a.constructor == Array && a
actually "returns" a if the first comparison is true. It is probably better phrased as "evaluates to a" but it may be easier explained as "returns".

Similarly,
(a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a )
will go through all the conditions, and if they all hold true return the result of calling the makeArray function.

The || operator ties it all together and ensures the first clause that evaluates to something is chosen as input to the function.

You can use Array.push to add array elements to any object

The next interesting snippet is inside the setArray function itself:
setArray: function( a ) {
	this.length = 0;
	[].push.apply( this, a );
	return this;
}


Look at [].push.apply( this, a );. What happens here is that the array literal's "push" method is used to push element a onto the this object which isn't an array but a jQuery object. That's right, push is generic and can be used with any object. It means the object gets a ".length" property like an array has and can be iterated with for loops. Example:

var obj = new Object(); /* Yes: an object, not an array. */
var str='';
Array.prototype.push.call(obj, 'Hello');
Array.prototype.push.call(obj, ' World');

for(var i=0;i<obj.length;i++){
	str+=obj[i];
}
alert(str);


Array methods being generic is part of the amazing flexibility of JavaScript, but this feature of the language might not be well known among authors.

About the jQuery code, I wonder if their approach of creating an empty array literal to use its push method is slower or faster than my Array.prototype lookup. Perhaps the question will inspire a reader to do some performance testing?

real *nix devs don't test in IE

, ,

Over at basket.kde.org they are understandably Windows-averse. I guess their website is not exactly targeted at IE users, but even so some cross-browser testing would uncover bugs and errors in their JS. Snippet:

    // Cross-browser implementation of element.addEventListener()
    function addEventListener(element, type, expression, bubbling) {
     bubbling = bubbling || false;
     if (window.addEventListener) { // Standard
      element.addEventListener(type, expression, bubbling);
      return true;
     } else if (window.attachEvent) { // IE
      element.attachEvent('on' + type, expression);
      return true;
     } else return false;
    }


Whoever wrote that obviously is confused about event bubbling versus event capturing (luckily it defaults to a sensible false!) but the main problem with this code is the line

if (window.addEventListener) { // Standard


Um, no, what you are seeing there is not the W3C standardised window.addEventListener1. You're actually checking for the existence of this very function - the one we're inside when we hit that statement. Naturally IE chokes on the next line and no event handlers are added. (If you ask, it should read if(element.addEventListener).)

So - a slick, good-looking production site that wasn't tested with IE - what a rarity! p

Edit - note 1: well, actually W3C didn't specify addEventListener for window in the first place, it remains a Gecko extension like I've complained about earlier so the comment "// Standard" is doubly wrong..