Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at NCDevCon 2011 (Raleigh, NC) with:

Overriding Internal ColdFusion Component Variables With Method Arguments

By Ben Nadel on
Tags: ColdFusion

One of the many cool things that Elliott Sprehn talked about in his CFUNITED 2010 presentation was the fact that ColdFusion component variables could be overridden using method arguments. After I saw this, I tried to experiment with this concept on my own. At first, I could not get it work. Elliott's presentations were all done in ColdFusion 9, and as Adam Tuttle pointed out, there have been some significant changes in scope prioritization going from ColdFusion 8 to ColdFusion 9.

To demonstrate both Elliott's variable highjacking and Adam's scope prioritization changes, I have put together some test code that I will run in both ColdFusion 8 and ColdFusion 9.

Non-Scoped Private ColdFusion Component Variables

In this first demo, I have created a Girl.cfc ColdFusion component that has one public method - sayHello(). This method returns a different response based on the internal mood of the Girl as represented by the private method, getMood():

Girl.cfc (Non-Scoped Private Variable References)

  • <cfcomponent
  • output="false"
  • hint="I am a beautiful girl object."
  • flickr:headshot="/photos/51421058@N02/4920811307/lightbox/">
  •  
  •  
  • <!--- Set private variables. --->
  • <cfset variables.name = "Vicky" />
  •  
  •  
  • <cffunction
  • name="getMood"
  • access="private"
  • returntype="numeric"
  • output="false"
  • hint="I return a numeric representation of the mood.">
  •  
  • <!--- Return a random mood. --->
  • <cfreturn randRange( 1, 10 ) />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="sayHello"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="I say hello to the given person.">
  •  
  • <!---
  • Get the mood so we can determine how to respond.
  •  
  • NOTE: In the following code, none of our private
  • variables (getMood() and name) are being scoped.
  • --->
  • <cfif (getMood() gt 6)>
  •  
  • <!--- Good mood - respond favorably. --->
  • <cfreturn "Hello, I'm #name# - nice to meet you." />
  •  
  • <cfelse>
  •  
  • <!--- Bad mood - respond coldly. --->
  • <cfreturn "I'm sorry, I don't have time for you." />
  •  
  • </cfif>
  • </cffunction>
  •  
  • </cfcomponent>

As you can see, the sayHello() method reference two, unscoped private variables: getMood() and name. To see if these private variables could be overridden, I wrote a test script that passes in like-named arguments:

  • <!--- Create the girl instance. --->
  • <cfset girl = createObject( "component", "Girl" ) />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <h2>
  • Standard Girl.sayHello()
  • </h2>
  •  
  • <!---
  • Say hello a number of times to get a number of random
  • responses based on mood.
  • --->
  • <cfloop
  • index="i"
  • from="1"
  • to="10">
  •  
  • <cfoutput>
  •  
  • Response: #girl.sayHello()#<br />
  •  
  • </cfoutput>
  •  
  • </cfloop>
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <h2>
  • Override Variables With Arguments
  • </h2>
  •  
  •  
  • <!---
  • Define a custom method to override the internal getMood()
  • method of the Girl object. This custom UDF will always return
  • "10" for mood.
  •  
  • NOTE: I am leaving out a lot of the attributes here simply
  • for ease of demonstrtation.
  • --->
  • <cffunction name="getMood">
  • <cfreturn 10 />
  • </cffunction>
  •  
  •  
  • <!---
  • Say hello a number of times to get a number of random
  • responses based on mood. This time, however, we are going
  • to pass in arguments with names that conflict with private
  • scoped variables in the Girl object.
  • --->
  • <cfloop
  • index="i"
  • from="1"
  • to="10">
  •  
  • <cfoutput>
  •  
  • <!--- Override NAME and GETMOOD variables. --->
  • Response: #girl.sayHello(
  • name = "Joanna",
  • getMood = getMood
  • )#
  •  
  • <br />
  •  
  • </cfoutput>
  •  
  • </cfloop>

The first part of the code is just a control - I am invoking the sayHello() method without any arguments to make sure that I get a nice, random set of responses. In the second part of the demo, however, I have defined an external version of the getMood() method which always returns "10"; then, I am invoking the sayHello() method, this time passing in custom "name" and "getMood" arguments.

When I run this code in ColdFusion 8, I get the following page output:

Standard Girl.sayHello()

Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.

Override Variables With Arguments

Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.
Response: Hello, I'm Vicky - nice to meet you.
Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.

As you can see, nothing special has happened; the sayHello() method continues to use the private versions of "name" and "getMood" even when I pass in like-named arguments. In essence, ColdFusion is searching the ColdFusion component's private Variables scope for non-scoped variable references before it searches the Arguments scope (where our overriding values can be found).

NOTE: The ColdFusion 8 documentation puts the Arguments scope before the Variables scope in search order. This is documentation is clearly inaccurate.

When I run this code in ColdFusion 9, I get the following page output:

Standard Girl.sayHello()

Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.
Response: Hello, I'm Vicky - nice to meet you.
Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.

Override Variables With Arguments

Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.

Here, we see an entirely different behavior; in the second half of our demo code, ColdFusion is clearly using the custom "name" and "getMood" arguments that we passed-in when invoking the sayHello() method. It seems that in ColdFusion 9, the Arguments scope now takes higher precedence when searching for non-scoped variable references. In essence, we can now use externally-defined Arguments in order to highjack the internal logic of a ColdFusion component.

Variables-Scoped Private ColdFusion Component Variables

In the previous demo, we were using non-scoped, private variables within our sayHello() method; but, what if we used more explicit, variables-scoped values? For performance and readability reasons, it is always recommended that you scope your values; but, will scoping prevent us from overriding those private variables?

Girl2.cfc (Variables-Scoped Private Variable References)

  • <cfcomponent
  • output="false"
  • hint="I am a beautiful girl object."
  • flickr:headshot="/photos/51421058@N02/4920811307/lightbox/">
  •  
  •  
  • <!--- Set private variables. --->
  • <cfset variables.name = "Vicky" />
  •  
  •  
  • <cffunction
  • name="getMood"
  • access="private"
  • returntype="numeric"
  • output="false"
  • hint="I return a numeric representation of the mood.">
  •  
  • <!--- Return a random mood. --->
  • <cfreturn randRange( 1, 10 ) />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="sayHello"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="I say hello to the given person.">
  •  
  • <!---
  • Get the mood so we can determine how to respond.
  •  
  • NOTE: This time, both of our private variables (getMood()
  • and name) are being scoped to the Variables scope.
  • --->
  • <cfif (variables.getMood() gt 6)>
  •  
  • <!--- Good mood - respond favorably. --->
  • <cfreturn "Hello, I'm #variables.name# - nice to meet you." />
  •  
  • <cfelse>
  •  
  • <!--- Bad mood - respond coldly. --->
  • <cfreturn "I'm sorry, I don't have time for you." />
  •  
  • </cfif>
  • </cffunction>
  •  
  • </cfcomponent>

As you can see here, the only difference that I've made in this Girl2.cfc ColdFusion component is that the "name" and "getMood" values are now explicitly scoped to the Variables name space.

With this new internal logic, I had to change the test code a bit; now, rather than passing in "name" and "getMood" arguments individually, I am passing in a single, "Variables," argument:

  • <!--- Create the girl instance. --->
  • <cfset girl = createObject( "component", "Girl2" ) />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <h2>
  • Standard Girl.sayHello()
  • </h2>
  •  
  • <!---
  • Say hello a number of times to get a number of random
  • responses based on mood.
  • --->
  • <cfloop
  • index="i"
  • from="1"
  • to="10">
  •  
  • <cfoutput>
  •  
  • Response: #girl.sayHello()#<br />
  •  
  • </cfoutput>
  •  
  • </cfloop>
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <h2>
  • Override Variables SCOPE With Arguments
  • </h2>
  •  
  •  
  • <!---
  • Define a custom method to override the internal getMood()
  • method of the Girl object. This custom UDF will always return
  • "10" for mood.
  •  
  • NOTE: I am leaving out a lot of the attributes here simply
  • for ease of demonstrtation.
  • --->
  • <cffunction name="getMood">
  • <cfreturn 10 />
  • </cffunction>
  •  
  •  
  • <!---
  • Say hello a number of times to get a number of random
  • responses based on mood. This time, however, we are going
  • to pass in arguments with names that conflict with private
  • scoped variables in the Girl object.
  • --->
  • <cfloop
  • index="i"
  • from="1"
  • to="10">
  •  
  • <cfoutput>
  •  
  • <!--- Create a variables struct. --->
  • <cfset myVariables = {
  • name = "Joanna",
  • getMood = getMood
  • } />
  •  
  • <!--- Override VARIABLES scope. --->
  • Response: #girl.sayHello(
  • variables = myVariables
  • )#
  •  
  • <br />
  •  
  • </cfoutput>
  •  
  • </cfloop>

As you can see, before I invoke the sayHello() method, I am assembling a structure that contains the custom "name" and "getMood" values; then, when I invoke the sayHello() method, I am passing this structure in as the named-argument, "Variables." The idea here is that we are going to be highjacking the Variables scope itself rather than individual values within it.

When I run code in ColdFusion 8, I get the following page output:

Standard Girl.sayHello()

Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.
Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.

Override Variables SCOPE With Arguments

Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.
Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.

As you can see, ColdFusion 8 still looks at the internal Variables scope before it looks at the arguments scope. As such, our attempts to highjack the internal logic are again thwarted.

When we run this code in ColdFusion 9, however, we get our new behavior:

Standard Girl.sayHello()

Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.
Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: I'm sorry, I don't have time for you.
Response: Hello, I'm Vicky - nice to meet you.

Override Variables SCOPE With Arguments

Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.
Response: Hello, I'm Joanna - nice to meet you.

Once again, we are seeing that ColdFusion 9 is searching the Arguments scope before it searches its own, internal Variables scope. The difference this time is that we are treating the "variables" reference like an unscoped value (as opposed to treating "name" or "getMood" like unscoped values).

According to both the ColdFusion 8 and ColdFusion 9 documentation, the Arguments scope should be searched before the Variables scope; as you can see in the demos above however, the ColdFusion 8 documentation is clearly wrong (Variables takes higher precedence that Arguments). It looks like they have finally corrected this in ColdFusion 9. This new, more accurate behavior now allows us to do some very interesting overriding which is what Elliot Sprehn was ultimately demonstrating with his Closure code.




Reader Comments

@Tony,

Yeah, I could see that it might; suddenly using Arguments before Variables could cause some crazy behavior if you're not expecting it!!

Reply to this Comment

I must have missed this I noticed "flicker:headshot" attribute when you were defining the cfcomponent tag. Is that a valid attribute name or some new language construct in ColdFusion?

Reply to this Comment

@William,

ColdFusion components and UDFs allow for custom attribute. Typically, you are suppose to name-space them (namespace:attribute); but, ColdFusion will allow them without name space - the name space is for collision protection in future releases of ColdFusion.

In my code demo, the flickr:headshot was just there as an easter egg ;)

Reply to this Comment

I feel like this new behavior can be useful in some situations (ie. implementing a strategy pattern or perhaps for closures?), but also a very dangerous in others. There doesn't seem to be a way now to ensure that 'protected' usage of private variables, remains private. I feel like encapsulation is being broken somehow by this change in scope traversing.

Am I off base here?

Reply to this Comment

@JAlpino,

It definitely can open up your code to some strange behavior if the calling context has a malicious intent; of course, you always need to remind yourself that the calling context is part of your application and *shouldn't* be malicious.

It will be interesting to see how many people run into scoping problems when they upgrade.

Reply to this Comment

Maybe there's something I'm missing with CF9 but isn't :

<code>
<!--- Set private variables. --->
<cfset variables.name = "Vicky" />
</code>

public and in the variables scope whereas this is private :

<code>
<!--- Set private variables. --->
<cfset variables.name = "Vicky" />
</code>

Reply to this Comment

DOH! Sorry....

Maybe there's something I'm missing with CF9 but isn't :

<!--- Set private variables. --->
<cfset variables.name = "Vicky" />

public and in the variables scope whereas this is private?

<!--- Set private variables. --->
<cfset local.name = "Vicky" />

Not trying to nit pick, just making sure I'm not misunderstanding some cool bit of information.

Reply to this Comment

Boo. I don't like this. I've always been a big proponent of scoping ALL variables in CFCs (except a function's local scope), but I've never applied that to method calls.

Reply to this Comment

@Allen,

Ah, I see what you're saying now. The new "local" scope in CF9 is the local scope to the currently executing CFFunction. It is "private" to the CFFunction in that nothing outside of the function can reference it (inherently).

But, as far as CFCs go, "variables" is the private and "this" is the public scope.

@Joshua,

Well, keep in mind that we are intentionally messing with things here - we are meaning to get odd behavior. I don't think this necessarily means you should stop doing what you're doing.

Reply to this Comment

Cool-cool. Thanks @BenNadel!

That all makes sense. I just wanted to make sure I wasn't somehow misunderstanding the new local scope in CF9 versus variables. *whew*

Reply to this Comment

@Allen,

No problem at all. The new LOCAL scope is pretty much just a reaction to the trend that many developers were doing this at the top of their functions:

<cfset var local = {} />

Although, now that we can explicitly reference the local scope, it will be interesting to see how we can abuse it :)

Reply to this Comment

The thing that is extremely disturbing to me is that in your second example, you explicitly used #variables.getMood()# and #variables.name# within the sayHello() function, and yet Coldfusion still used the arguments scope instead.
This seems extremely insecure and give's me no recourse to privatize my variables. I assume the local scope behaves the same way?

Reply to this Comment

@Brian,

I am not sure about the local scope itself. I only turn on CF9 for occasional testing (still in CF8 mostly). I'll see what kind of shenanigans can be had at the Local scope's expense.

As far using an Argument named "Variables," yeah, that is kind of interesting. Imagine passing in a "hidden" CFC named Variables that has its own "isUserAuthorized()" method?? Then, in the other CFC Method, it might have something like:

<cfif variables.isUserAuthorized()>
. . . . Do something secure . . .
</cfif>

... of course, you've totally hijacked the isUserAuthroized() method call to return TRUE by passing in an overriding argument.

Very interesting, right?

Reply to this Comment

@Brian & @Ben,

After reading your blog post Ben, I got very interested in this "problem" (though it's mostly a problem if a given method's arguments come from an untrusted source.)

I looked at ways of securing a method's read/write to values in the Variables scope and this is what I came up with--pardon the cfscript:

(These are defined in a component, but this could also apply to the Variables scope and methods in a regular cfm page.)

  • variables.field1 = "value";
  • variables.field2 = 1;
  • variables.field3 = true;
  • variables.field4 = false;
  •  
  • /* Can be intercepted by passing in a value assigned to named argument "field1".
  • * e.g.: obj.getField1(field1 = "otherValue")
  • * returns "otherValue instead of "value".
  • */
  • public function getField1() { return field1; }
  •  
  • /* Can be intercepted by passing in a struct assigned to named argument "Variables"
  • * containing a value assigned to the key "field2".
  • * e.g.: obj.getField2(variables = { field2 = 0 })
  • * returns 0 instead of 1
  • */
  • public function getField2() { return Variables.field2; }
  •  
  • // Cannot be intercepted by any arguments passed in.
  • public function getField3() { return GetPageContext().getVariableScope().field3; }
  •  
  • // Cannot be intercepted by any arguments passed in.
  • public function getField4() {
  • local.variables = getPageContext().getVariableScope();
  •  
  • return variables.field4;
  • }

Ultimately the solution I came up with uses the getPageContext().getVariableScope() method to independently get a reference to the CFPage object's (i.e.: your executing cfm or cfc file.) Variables scope, rather than relying on the assumption that variables.foo will reference the variable foo in the Variables scope.

The first two getter methods are just rehashes of what Ben has already shown us in the blog post.

The third method shows that you can get variables out of the Variables scope by using getPageContext().getVariableScope().foo (or whatever your variable name is), because that method returns a reference to the Variables scope which is of course a ColdFusion struct. I wasn't super thrilled with this because it forces extra verbosity on a developer when all they really want to do is simple ensure that a variable their method is dealing with is actually in the Variables scope.

The fourth method demonstrates assigning a reference to the Variables scope in the method's Local scope, and because Local is always searched first in a method, your Variables scoped variables (say that 10 times fast) will hit local.variables.VariableName before they hit arguments.variables.VariableName, protecting them from being intercepted by arguments passed to the method. This seemed a lot cleaner and really only needs to be used if a method must ensure it's working with the Variables scope.

What do you guys think?

Reply to this Comment

@Graeme,

Very cool approach; it would have never occurred to me to get the page context in order to get the valid Variables scope. Nice thinking.

Of course, as you say, this really only matters when the invoking context is an "untrusted" source. I just don't want everyone thinking that this kind of approach needs to be used in every situation (there's so many ways to circumvent component "encapsulation" that to worry about them could easily drive you mad).

Way to think outside the box!

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.