Earlier today a buddy of mine tweeted about an interesting Drupal plugin he found - Misery. I'm not a Drupal user but anything named Misery sounds pretty cool. Turns out the plugin is an interesting way to to fight against trolls. Anyone who runs a site that allows for user content will know what kind of trouble a bad troll can cause. Worse than a spammer, they can slip by undetected from your conventional blocking methods and slowly poison a site. While I'm not sure you can accurately detect a troll automatically, if you did have a way of flagging a troll then a plugin like this could be kind of cool. And if it can be done in PHP, then certainly we can build it in ColdFusion.

First, let's look at what the plugin does. Based on some troll marker, it will:

  • Delay page execution
  • Return a blank white screen
  • Return the wrong page
  • Return a random node (a Drupal term - imagine it was a blog and you ended up on the wrong entry)
  • Return a 403 or 404
  • Stop a form from submitting
  • Crash IE6

For my implementation I decided to recreate all of the above except for the random node and the IE6 crash. As it stands, IE6 has a pretty good chance of crashing by itself anyway so why bother trying to force it. I began by creating a simple component that randomly picked a way to annoy the troll:

component {

variables.randomUrls = "";

public function init() { writelog(file="application", text="Misery init"); }

public function setRandomUrls(string s) { variables.randomUrls = arguments.s; }

public function enactMisery() { var rnd = randRange(1,100); if(rnd < 40) { writeLog(file="application",text="Misery: Delay"); sleep(1000 * randRange(1,4)); } else if(rnd < 50) { writeLog(file="application",text="Misery: White Screen of Death"); abort; } else if(rnd < 60) { writeLog(file="application",text="Misery: Wrong page"); if(len(variables.randomUrls)) { var newurl = listGetAt(variables.randomUrls, randRange(1, listLen(variables.randomUrls))); location(url=newurl,addtoken=false); } } else if(rnd < 65) { writeLog(file="application",text="Misery: 403 Header"); include "403header.cfm"; } else if(rnd < 70) { writeLog(file="application",text="Misery: 404 Header"); include "404header.cfm"; } else if(rnd < 80) { writeLog(file="application",text="Misery: Kill random form field."); var keys = structKeyList(form); if(len(keys)) { var toKillNum = randRange(1, listLen(keys)); for(var i=1; i<=toKillNum; i++) { var chosen = randRange(1, listLen(keys)); var chosenKey = listGetAt(keys, chosen); writeLog(file="application",text="Misery: Removing form.#chosenkey#"); structDelete(form, chosenKey); keys = listDeleteAt(keys, chosen); } } }

writelog(file="application", text="Done with Misery"); }

}

Going from top to bottom, you can see I've got a blank init that simply logs to the Application log. I've got a setRandomURLs function that allows me to specify what random URLs should be used when that option is fired. The real core of the component is the enactMisery function. (And yes, I love the method name.) It simply selects a random act of cruelty to apply to the user. The pause is simple enough with sleep. The white page of death is even simpler. The random redirector picks a URL from the list your code provides. If you don't then this option will simply be skipped. My headers have to use an include since there is no built in way to do cfheader in script. You can do it easily enough with a few Java calls but I wrote this quickly and didn't want to bother. I'll include both of those templates below. The real evil method is the last one. If form data exists, this branch will remove random keys from the post. This is truly evil as it will not repeat exactly the same and possibly drive the troll insane. Before I show my demo code, here is the first header file, 403header.cfm:

<cfheader statuscode="403" statustext="Forbidden Because"> <html><head><title>403</title></head><body> <h1>403 Forbidden Because</h1> of something... </body></html> <cfabort>

And here is the 404 version:

<cfheader statuscode="404" statustext="Missing Page"> <html><head><title>404</title></head><body> <h1>404 Missing</h1> That important page you wanted is missing. </body></html> <cfabort>

Ok, now let's look at my demo. First I'll begin with the page the user will actually run, index.cfm:

<h2>Welcome</h2>

<p> Nothing special is going on - promise. </p>

<form action="index.cfm" method="post"> your name: <input type="text" name="name"><br/> your age: <input type="text" name="age"><br/> your foo: <input type="text" name="foo"><br/> your goo: <input type="text" name="goo"><br/> <input type="submit" name="submit" value="Send Important Comments"> </form>

<cfif structKeyExists(form, "submit")> <cfdump var="#form#" label="Form"> </cfif>

The index page is just some text and a form. If a form post is detected (by checking the submit button), I dump the results. Now let's look at my Application.cfc:

component { this.name="misery"; this.sessionManagement="true";

public boolean function onApplicationStart() { application.miseryService = new misery(); application.miseryService.setRandomUrls("http://www.cnn.com,http://www.yahoo.com"); return true; }

public boolean function onRequestStart(string req) { if(structKeyExists(url,"troll")) { session.miseryFlag = true; }

if(session.miseryFlag) application.miseryService.enactMisery();

return true; }

public boolean function onSessionStart() { session.miseryFlag = false; return true; } }

This is fairly straightforward. My onApplicationStart creates an instance of misery (and yes, I like that code too) and passes in random URLs. For my onRequestStart I use a simple URL hack to enable the troll hack. For those of you testing, just add ?troll=ios to turn yourself into a troll. If the flag is active, then the Misery component's enactMisery method is fired. And that's it. Demo this below - and remember to add ?troll=gruber to turn on cruel mode.