TortoiseSVN, be silent!

TortoiseSVN routinely scares the crap out of me. I have my headphones on, I’m in the zone, I’m checking in changes, BEEEEEEEEAAAUUUUWWWWW!

By default, when you install TortoiseSVN it is set to play sounds any time error, warning, or notice event occurs. This is pretty bad and, as I mentioned already, quite startling. For whatever reason, instead of just putting an option into the application where you can change them directly you have to go to the windows sounds dialog to shut them off:

sounds.jpg

This is along the same lines as stealing my focus. Applications should not play sounds by default unless the user wants or expects them to. For example, I’d expect a music player to make some sounds but I never expect my file repository browser to do so.

If you write applications, random and sudden sounds are generally bad and undesired. There are some exceptions, however they are rare.


Rebuttal: We're not "resources"

Recently a friend/colleague wrote a post about how we, as workers, are not “resources”. If you click over and read his post first the rest of this post will make much more sense.

The underlying point behind his post is that people are not readily interchangeable. He gave many examples of people or professions that are not easily substitutable: Derek Jeter at short stop, Steve Jobs at Apple, Brad Pitt as an actor, Michael Jordon as a shooting guard. This point I agree with wholeheartedly. People are rarely interchangeable, at least in a thought industry, but they are still resources.

If we take a look at the semantics of the terminology “resources” we see that it has many meanings, many of which can easily be tied back to employees. Let’s step through the two most relevant of these.

1. A source of supply, support, or aid, especially one that can be readily drawn upon when needed
When viewed from the perspective of a company employees serve one purpose: to supply a service that the business needs in order to operate. How many times have you been called upon by your employer to complete a task that “just came up”? If it is more than once that you fall into the “one that can be readily drawn upon as needed” category. This definition fits pretty closely with most employees in thought industries.

2. Money or any property that can be converted into money
Employees garner a wage in exchange for a service they provide. Under this arrangement a certain amount of money is invested and tied up in that employee. This is cash the business cannot spend if they wish to continue to deliver paychecks. If the employee-employer relationship is terminated then the cash that was tied up in the employee is immediately available again. To put it another way, the employee “resource” was converted to cash.

For example, let’s say an engineering team has a budget for an engineer slot of $100,000. There are three people vying for this position. John is a mediocre programmer and will work for $55,000. Peter is a very good programmer and will work for $98,000. Greg is the programming equivalent of Derek Jeter or Michael Jordon and will work for $123,000. Greg is clearly the best choice if the team wants the best possible talent, however that option is not cost effective. So the company moves to Option B, Peter, who will fit within the budget and does a very good job. The company has now purchased a resource for $98,000 per year. If this employee discontinues his employment, the company will immediately regain $98,000 per year.

In addition, sports players (e.g. Derek Jeter, Michael Jordon, etc.) are routinely traded in order to deal with financial issues. While each “resource” is valued differently according to their ability, they are still treated as an item that can be converted to cash as needed. This definition also fits with what is found in thought industries.


If I build a fence I can choose to go with the cheapest lumber, nails, and concrete. Using cheap resources will be more cost effective but will likely yield an inferior fence that will not withstand the element for long. Conversely, I can choose the best resources and may find that it is highly inefficient financially but that the fence will still be around 100 years from now. Taking a more modest approach, I can choose median resources that make the fence durable for a long time and cost effective.This is no different for people.

If the Yankees where to pencil Mark Turansky in at short stop they would have a cheap resource but may suffer in the quality of their infield. However, if they were to choose Derek Jeter they would pay considerably more for the resource but would likely recognize a considerable improvement in the quality of their team. These people are “resources” for the reasons named above, however that doesn’t necessarily mean it is prudent to treat them interchangeably.

The problem with using the word “resources” to identify employees is that it objectifies them. This is such a broad term that it could just as easily mean calculators, pencils, or laptops as it could employees. As noted in Mark’s post, Steve McConnell “ranks ‘Weak Personnel’ as the 2nd classic mistake an organization can make when trying to build software”. The word personnel here is simply a narrowing term to indicate that the resources in question are human in nature and not of the inanimate variety. I will agree with the fact that people shouldn’t be treated as resources, even though we are just that. Our particular variety of resources comes with emotions installed, which makes it harder to blindly objectify us without some emotional responses following closely.

We are all “resources”, albeit differently valued ones that come with emotions attached.


Writing A JavaScript Obfuscator

I recently posted about a bot-safe email link where I showed how you can use a series of obfuscated method calls through JavaScript to pop open a mail client with an address in the TO field, while still maintaining a decent sense of privacy. In this post we will dive into how the actual obfuscation script works and how it can be extended.

I show how I use it in the bot-safe email link but the structure of the script is a good foundation for any JavaScript obfuscation needs.

Generating Function Names
The core part of the system comes from the generation of random numbers and letters. Because of this we will use the following extensively:

/*
 * Generates a random letter, either upper or lower case.
 */
function randomLetter() {
	// Generate a letter between A-Z (Ascii 65-90) or a-z (Ascii 97-122). We
	// will randomly select between upper and lower case.
	if (Math.round(Math.random() * 1) == 1) {
		return String.fromCharCode(97 + Math.round(Math.random() * 25));
	}

	return String.fromCharCode(65 + Math.round(Math.random() * 25));
}

/*
 * Generates a random number between 0 and 9.
 */
function randomNumber() {
	return Math.round(Math.random() * 9);
}

These two functions are the enabling factor behind our method name generator:

/*
 * Generates a random function name that always begins with a letter and
 * is composed entirely of random numbers and letters and is of random
 * length.
 */
function randomFunctionName() {

	// Max function name length
	var maxLen = 25;

	// Generate a random number - this will be the length of our method. We
	// will subtract one right off the bat to account for the first letter
	// in the method.
	var len = Math.round(Math.random() * maxLen) - 1;

	// Generate a random letter - functions can't start with a number so we
	// will always start with a letter.
	var functionName = randomLetter();

	// Loop through and create the method name.
	for (i = 0;i < len;i++) {
		// Randomly select between a letter and a number
		if (Math.round(Math.random() * 1) == 1) {
			functionName += randomLetter();
		} else {
			functionName += randomNumber();
		}
	}

	return functionName;
}

Now a simple call to randomFunctionName() will give a completely randomized, yet valid, function name between 1 and 25 characters in length. We will use this extensively throughout the rest of the script.

Generating Methods to Obfuscate the Email Address
Next we need to write a method to randomly parse through the email address, splitting into sections that can be pushed into their own methods. In addition, we will need to vary up the methods by which the method is created. We will do this by going through a series of loops which split portions of the text out of the mail string, generate a function, and have the portion of the string return from the function in one way or another.

This method is a bit involved, so we’ll just take a look at it then step through the parts of importance. Please note that I have removed comments, spaces, and other things so that it will render better. For the full code visit the actual script and view source.

function generate(addr) {
	var loc = "mailto:" + addr;

	// This will keep track of all the generated javascript code
	var functions = "";
	var methodCalls = "";

	var maxSplit = 3;

	var spos = 0;
	var epos = 0;
	var splitLen = 0;

    // This is the number of method aggregations we want - min of 1, max of 3.
    var aggregations = Math.round(Math.random() * 2) + 1;

    // This holds a set of function names that may or may not be aggregated
    // into a single call through another method.
    var functionSubset = "";

    // Split up the string into multiple parts in order to start breaking
    // it up into multiple methods. Each one of these will become a part
    // of the main method call to recompose the email address.
    var cnt = 0;
    do {
        cnt++;

        // If the length from the last split to the end of the string is
        // less than the max split then we will want to use that length
        // for the next split.
        if (epos > -1 && loc.substring(epos).length <= maxSplit) {
            splitLen = loc.substring(epos).length
        } else {
            splitLen = maxSplit;
        }

        // Determine the length for the first split
        splitLen = Math.round(Math.random() * splitLen);

        // Grab the split
        spos = epos;
        epos = spos + splitLen;
        var str = loc.substring(spos, epos);

        // Create a function name and add it to the list of functions that
        // must be called by the mail method.
        var functionName = randomFunctionName();
        if (functionSubset.length > 0) {
            functionSubset += " + ";
        }
        functionSubset += functionName + "()";

        functions += "\nfunction " + functionName + functionContents(str);

        // Tracks whether we need to reset the aggregation variables or not
        var resetVars = false;

        // If there is only one aggregation then we will just add the method
        // to the emailMe() method call list. Also check to see if we have
        // aggregated enough to chop and create a method to hold the functions
        // we have created thus far.
        if (aggregations == 1 || epos >= loc.length) {
            // Add the function call to the emailMe() method.
            if (methodCalls.length > 0) {
                methodCalls += " + ";
            }
            methodCalls += functionSubset;
            resetVars = true;
        } else if (cnt == aggregations) {
            // Get a function name for the aggregate function.
            var aggregateFunctionName = randomFunctionName() + "()";

            // Add the aggregate function to the emailMe() method call.
            if (methodCalls.length > 0) {
                methodCalls += " + ";
            }
            methodCalls += aggregateFunctionName;

            // Add the actual function
            functions += "\nfunction " + aggregateFunctionName + " { return " + functionSubset + ";}";

            resetVars = true;
        }

        if (resetVars) {
            // Reset the cnt and functionSubset and figure a random aggregations
            // number
            cnt = 0;
            aggregations = Math.round(Math.random() * 2) + 1;
            functionSubset = "";
        }

    } while (epos >= 0 && epos < loc.length);

    functions = "// Remove this before placing code on page >>>\nemailMe();\n// <<< Remove this before placing code on page\n\n\n"
                + "function emailMe() { window.location = " + methodCalls + "; }" + functions;

	document.frm.code.value = functions;
}

The methodCalls variable on line 6 simply accumulates the calls that will be made from the emailMe() method. That is, the emailMe() method will ultimately contain “return ” + methodCalls, as shown in the code block below.

The maxSplit variable on line 8 is used to determine how many characters are the maximum number allowed in each substring. The number 3 was settled upon because it generally ensures that the entire email address cannot be in the same substring and it guarantees that the mailto: token will be broken into at least two parts.

The aggregations variable on line 15 signifies the number of functions that should be aggregated under a single function. If this were set to 1 on each iteration then the result would be that the emailMe() method would call each function, adding the results of them all together to result in the email address:

function emailMe() {
    return s1() + s2() + s3();
}

By having this evaluate to higher than one on some iterations, portions of the generated functions will be delegated to other method calls, making the emailMe() method need to call fewer methods directly:

function emailMe() {
    return s1() + a1();
}

function a1() {
    return s2() + s3();
}

Our loop to gather substrings actually starts on line 25 and will continue until the end of the string is reached. Within this loop the actual parsing and function generation takes place. Lines 31-38 determine how long the next substring should be, taking into account the remaining length of the string. A random function name is then generated and the function is actually generated by a call to functionContents() on line 53, which we will go into with more detail shortly. The actual function itself is placed within the functions variable for storage. The generated function names are added to a functionSet variable that simply contains a list of function calls. These will either be placed into an aggregate method or in the emailMe() method, as discussed above. The code determines whether to add the function to an aggregate method, keep accumulating, or add it to the emailMe() method in lines 61-83.

Finally, the last two lines of the function enable retrieval and testing of the generated code. The first line creates a call to emailMe() that resides outside a function so when the block of code is sent through the eval() function something actually happens. The second line of code simply places the entire thing into the text area for viewing, editing, and copying.

Generating Function Contents
Now that we have broken the pieces of our mailto string up into many substrings and generated a function for each the next step is to fill in the contents of each function. I chose to do this by having the actual loop itself call out to the functionContents(str) method, which then delegates off to a number of different techniques for returning the string. The technique used is chosen at random, making each call to the functionContents(str) method capable of returning a different result.

Each of the methods is illustrative in nature and doesn’t really do anything spectacular, aside from making the end product harder to read and parse with the human mind.

Here is the code:

/*
 * Generates the contents of each function by using one of a few random
 * techniques for obfuscating the internal data. Every technique used
 * here must resolve back to a string if the function itself is called.
 */
function functionContents(str) {
    var technique = Math.round(Math.random() * 2);

    var f = "() {"

    switch (technique) {
        case 0:
            f += escapeFunctionContents(str);
            break;
        case 1:
            f += staticFunctionContents(str);
            break;
        case 2:
            f += evalFunctionContents(str);
            break;
    }

    f += "}";
    return f;
}

function escapeFunctionContents(str) {
    return "return unescape(\"" + escape(str) + "\");";
}

function evalFunctionContents(str) {
    return "return eval(\"if (true) '" + str + "';\")";
}

/*
 * One of a few ways the return values of a function is formulated. This
 * method will simply return the passed string as a string.
 */
function staticFunctionContents(str) {
	return "return \"" + str + "\";";
}

The first technique escapes the string in our script and uses the unescape() method to return it to normal when the generated script is executed. This will typically not have much of an effect unless there are special characters in the email address – this method was used more for illustrative purposes than anything else.

The second technique uses the eval() function to evaluate a string. In our case I simply have it evaluate an if statement that always returns true, resulting in the eval() method returning the product of the true if statement, our substring.

The third technique simply returns the substring itself.

The techniques for generating the function bodies can be extended by simply adding a new function then updating the functionContents(str) method to take it into account as an option.

The GUI
We have the completed code and just need a way to launch and test it. I created a simple form that invokes the generate(str) method and places the output into a text area in a form that it can be executed through an eval() statement by clicking on the Test button.

<script> // All the stuff we went over above... </script>

<h1>McDonaldLand</h1>
<h3>Get all your info from McDonaldLand<br/><a href="http://www.mcdonaldland.info">www.McDonaldLand.info</a></h3>
<form name="frm">
	Enter the email address to be obfuscated:
    <input type="text" name="addr" value=""/><input type="button" value="Generate Code" onClick="generate(this.form.addr.value)"/><br/><br/>

    <textarea rows="20" cols="60" name="code"></textarea><br/>
    <input type="button" onClick="eval(this.form.code.value)" value="Test"/>
</form>

The Test
You can either follow along with the post (I didn’t test this one out) or you can copy the working script by viewing source (I tested this thoroughly). Either way the end result should be a page that will allow you to enter an email address, generate script from it, then test the script, all from the same page.