I have in my cfm something like this
<CFModule name="MyModule"
someParam_one="#something.one#"
someParam_two="#something.two#"
someParam_etc="etc_etc_etc"/>
And inside my module, I have an
<CFSet param_name = "someParam_one">
...
evaluate("attributes." & param_name)
On most of our servers, this work. But on one of our servers, I get a
Error resolving parameter ATTRIBUTES.SOMEPARAM_NAME
Any ideas why?
Thanks
Have you verified that someParam_one is actually getting created? I've found, for example, that if I do something like this:
<cfset foo = myObject.getSomething() />
and getSomething returns a void value or runs a Java function that doesn't return anything, that CF will choke on it. The variable will be "defined", or so the application seems to think, but attempting to access it will throw an error. So do the following to track down and catch the problem:
Dump your attributes scope to make sure that what you want is indeed actually there.
Run a StructKeyExists(Attributes, param_name) before attempting to access the variable.
Get rid of the evaluate, and instead use Attributes[param_name]
Tangential to your question, but Evaluate() is evil, and an unnecessary evil in this situation. You can write this instead, and it will be more clear, more secure, and faster:
<cfset param_name = "someParam_one">
...
<cfset param_value = Attributes[param_name]>
A shot in the dark:
There's a bug in CFMX where if you
make a CFMODULE call to a template (or
use custom tag) from within a CFC and
that tempate uses the CALLER scope to
return data, the data is never
available to the CFC function. This is
bug 51067 and it is related to the
VARIABLES scope bug, 45138.
Seen in the user comments in the CFMX 6 docs on CFMODULE.
Ok, we did something really stupid :-)
We had two set of these files deployed and one was updated while the other was not, thus the error.
Thanks for all your help.
Related
I am have some old ColdFusion code. It was originally written for CF9, but is now running on CF 2016.
application.cfc
local.esapi = createObject("java", "org.owasp.esapi.ESAPI");
application.esapiEncoder = local.esapi.encoder()
Much later
Regular page
form.Reason = application.esapiEncoder.encodeForHtml(form.Reason);
I am thinking of replacing this with
form.Reason = encodeForHTML(form.Reason);
Do these function the same?
Yes, the encodeForX() functions use OWASP's ESAPI behind the scenes. encodeForHTML() is CF10+ and has a canonicalize argument, which takes the input down to its lowest factor. CF2016 added an encodeFor argument to a cfoutput tag for outputting that does similar. There's also the canonicalize() function that will throw an error that you can catch. That's useful for seeing if someone is trying to throw nefarious inputs at your form or site. I can't think of a legit reason for double- or multi-encoding an input, and I would interpret such as an attack. The argument in the encodeForX() function will take it down to its base evaluation, but it doesn't throw an error and just returns the resulting output. Personally, I'm not sure that there's much of an accidental way to pass a value that would be picked up by canonicalization, and I'd simply rather catch that attempt and kick that user off of my site.
https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-functions/functions-e-g/encodeforhtml.html
https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-functions/functions-c-d/Canonicalize.html
https://www.owasp.org/index.php/Category:Encoding
I initially posted this as an answer to this question earlier regarding Empty CGI.REDIRECT_URL on ColdFusion 2016. After thinking about it, I thought better of it since technically didn't answer the OP's question. Instead I decided to make it into a separate question, even though it's more of a commentary than a question. While this technically might not meet the full requirements of a Minimal, Complete, and Verifiable example and people might ding me with downvotes, I decided it was worth it anyway in the hope that it will become easier to find for future CFers who might encounter this. Thus preventing them from banging their head against the wall regarding this peculiar behavior of the CGI struct/scope.
With that said, the CGI struct/scope has some undocumented inconsistent behavior from other structs/scopes. Note that I personally take no credit for this discovery since I happened across this some time ago upon reading Ben Nadel's blog post on this. So all the information I'm posting here is already detailed there, but I wanted to write a nice summary here on SO.
Undocumented behavior 1 - Unlike other structures, if a CGI struct key doesn't exist, then it won't throw an error when referencing it.
In the OP's original question, he was wondering why cgi.REDIRECT_URL existed but was empty. As he eventually found out, it never actually existed. As a separate example you can execute this line of code without throwing an error. Not what you'd expect, huh?
<cfoutout>#cgi.THIS_IS_A_FAKE_KEY#</cfoutout>
So what's a CFer to do? Test for the key existence.
<cfif structKeyExists( CGI, 'THIS_IS_A_FAKE_KEY' )>
THIS_IS_A_FAKE_KEY exists
<cfelse>
THIS_IS_A_FAKE_KEY doesn't exist
</cfif>
Undocumented behavior 2 - Unlike other structures, if you dump the CGI struct, it won't display all the key/value pairs, it will only display a defined set of keys.
In the OP's case, he had a custom Apache CGI variable cgi.REDIRECT_URL that was used in his code prior to upgrading to CF2016 and was able to refer to it directly. However, I'm presuming if he dumped out the cgi struct, it wouldn't appear in the dump. In Ben Nadel's case, he also had a custom cgi variable called cgi.document_root that was passed through from a load balancer and was able to refer to it directly, but he also wasn't able to see the key when dumping the cgi contents.
So what's a CFer to do? Understand this and store it in the back of your mind so you won't get bitten when you dump the cgi contents and the key/value pair isn't there. Other than that, not much else.
I went in to the cfusion.jar file of ColdFusion. What I found there was a bit confusing.
CGI scope is not of a structure form as one would hope for.
This is how a call for CGI variable is handled. eg <cfoutout>#cgi.THIS_IS_A_FAKE_KEY#</cfoutout>
The normal valid CGI scope variables are the ones in this list and these will be initialized to "" by default.
private static final String[] names ="AUTH_PASSWORD","AUTH_TYPE","AUTH_USER","CERT_COOKIE","CERT_FLAGS","CERT_ISSUER","CERT_KEYSIZE","CERT_SECRETKEYSIZE","CERT_SERIALNUMBER","CERT_SERVER_ISSUER","CERT_SERVER_SUBJECT","CERT_SUBJECT","CF_TEMPLATE_PATH","CONTENT_LENGTH","CONTENT_TYPE","CONTEXT_PATH","GATEWAY_INTERFACE","HTTP_ACCEPT","HTTP_ACCEPT_ENCODING","HTTP_ACCEPT_LANGUAGE","HTTP_CONNECTION","HTTP_COOKIE","HTTP_HOST","HTTP_USER_AGENT","HTTP_REFERER","HTTP_URL","HTTPS","HTTPS_KEYSIZE","HTTPS_SECRETKEYSIZE","HTTPS_SERVER_ISSUER","HTTPS_SERVER_SUBJECT","LOCAL_ADDR","PATH_INFO","PATH_TRANSLATED","QUERY_STRING","REMOTE_ADDR","REMOTE_HOST","REMOTE_USER","REQUEST_METHOD","SCRIPT_NAME","SERVER_NAME","SERVER_PORT","SERVER_PORT_SECURE","SERVER_PROTOCOL","SERVER_SOFTWARE","WEB_SERVER_API" };`
Also all of these values also come from various java libraries javax.servlet, HttpServletRequest etc.
If the requested variable is not any of these after a bit of checks, ColdFusion goes to the request headers. You can see these using getHttpRequestData().headers. Then looks for a key there with hyphens(-) instead of the underscores(_) in the cgi key request. (If the key starts with http_, then the key in the request headers will be with out it like this http_x_forward in request header will be x-forward)
value = request.getHeader(name.replace('_', '-'));
From what I understand as far as ColdFusion is concerned the keys, mentioned in the first point are the recognized as part of the CGI scope. But when there is additional information passed from a Apache load balancer server to ColdFusion, those end up in the request headers. Since the java getHeader just returns a blank string (or something with data type undefined) instead of a undefined error, ColdFusion does not identify any of the keys is defined or not.
So if the key THIS_IS_A_FAKE_KEY is sent to ColdFusion from an intermediary such as an Apache server. You will find that in the getHttpRequestData().headers['THIS-IS-A-FAKE-KEY'] but not on the CGI scope dump.
That being said my personal opinion is that it is better to check directly in the getHttpRequestData().headers for custom CGI variables other than in the scope itself.
EDIT Thanks to Ageax for pointing out one of my test cases was invalid on my earlier revision of this post.
Great bit of detective work RRK! So I decided to perform an experiment to verify your finding by creating two loops. The first loop will display the key/value pairs from getHttpRequestData().headers and the second loop does the same using the corresponding key/value pairs from the cgi scope by replacing the - with _. Voila! as reported by RRK, we can see how you can obtain the values by either method. I made an updated gist and posted here for anyone interested.
<cfset httpHeaders = getHttpRequestData().headers>
<h3>getHttpRequestData().headers</h3>
<cfloop collection="#httpHeaders#" item="key" >
<cfoutput><strong>#Key#</strong> : #httpHeaders[key]#<br></cfoutput>
</cfloop>
<h3>cgi keys dash to underscore</h3>
<cfloop collection="#httpHeaders#" item="key" >
<cfset keyUnderscore = replace(key, "-", "_", "all")>
<cfoutput><strong>#keyUnderscore#</strong> : #cgi[keyUnderscore]#<br></cfoutput>
</cfloop>
I'm calling a web service through ColdFusion which returns an object, and I want to verify if one of the methods of this object exists as it won't always exist.
I found this source which seemed promising however based on my tests I can see the results are always negative and the method is never found when it's clearly there.
<cfif structKeyExists("#Result.getNotifications().getValidationResult(0)#","getField")>
Result is my underlying object, and my end goal is to verify if the method getField() exists.
Is there a clean way to do this as opposed to a try/catch?
Update:
Unfortunately, I am not sure IsInstanceOf() works with web services, due to the fact that CF uses a Proxy object to "wrap" the underlying web service class. If not, another simple option is to check class name. That avoids the ambiguity of checking for method name only (which could potentially exist in many different classes). Plus I suspect it may be more light-weight than IsInstanceOf() anyway.
<cfif compare(yourObject.getClass().name, "org.tempuri.ValidationResultField") eq 0>
Found ValidationResultField. do something
</cfif>
It looks like the dump contains several different types of objects/classes: ArrayOfValidationResult, ValidationResultField, etecetera. It sounds like what you are really trying to determine is which of those classes you are working with, so you know exactly what fields and methods will be available, per the web service definitions. Given that, I think IsInstanceOf() would be a more appropriate test, than checking for method names. More accurate as well. Nothing prevents two different classes from having the same method name. So even if method X or Y exists, there is still a possibility it may be a different class than expected.
<cfif IsInstanceOf(yourObject, "org.tempuri.ValidationResultField")>
do something
</cfif>
As far as I know, the mentioned structKeyExists approach only works if CF wraps the class internally, e.g. all instances of cfcomponent.
The only option left is to actually reflect the class:
<cftry>
<cfset Result.getNotifications().getValidationResult(0).getClass().getMethod("getField", javaCast("null", ""))>
<!--- method does exist --->
<cfcatch type="coldfusion.runtime.CfJspPage$UnsupportedBaseTypeException">
<!--- method does not exist --->
</cfcatch>
</cftry>
If the method doesn't exist, it throws UnsupportedBaseTypeException, which seems to be a follow-up of NoSuchMethodException.
Honestly, you might as well just invoke the method and catch it. Reflection comes with an additional overhead and you have to catch it anyway.
Like Miguel-F, I think this is something for getMetadata(). The following should return an array containing the respective functions of the object:
<cfset funcs = getmetadata(nameOfObj).functions>
The names of the functions are then funcs[1].name, funcs[2].name, and so on.
In general, you may obtain the metadata of all the functions of a webservice, given the URL of the WSDL, with something like
<cfhttp method="get" url="http://www.webservicex.net/globalweather.asmx?WSDL" result="res">
<cfset wsXml=xmlparse(res.filecontent)>
<cfset wsOperations = xmlsearch(wsXml,"//wsdl:operation")>
<cfdump var="#wsOperations#">
Another method you could look at (perhaps undocumented) is to get the method names from the class names in the stubs directory.
The code to run is:
<cfscript>
wsargs = structnew();
wsargs.savejava="yes";
</cfscript>
<cfset convert=createobject("webservice","url_of_wsdl",wsargs)>
Then figure out how to fish out the names from the stubs directory, {CF_INSTALL}/stubs. In my case, CF_INSTALL is C:/ColdFusion2016/cfusion/
I created a component object like this in page1.cfm, to call one of the cfc's functions: IsDigits:
<CFSET MyObj = New cfcomponents.MyComp.RecordValidation(Trim(session.userid)
,Trim(session.yr))>
<CFSET MyObj.IsDigits(st_SSN_and_BDates)>
After page1.cfm is finished processing, it goes to page2.cfm using:
<script>
window.location="page2.cfm
</script>
(I can't use cflocation because of cfflush that I use on almost every page).
In page2.cfm, I still need to call different functions that exist in the RecordValidation.cfc. I'm wondering if I have to run
<CFSET MyObj = New cfcomponents.MyComp.RecordValidation
... one more time?
There's no limit to how many times one can create objects. If you need another one in the next request... create another one!
Do you perhaps need to elaborate on the problem you're perceiving you're having, so we can give you a more exacting answer?
Also perhaps look into using something like DI/1 or WireBox to manage your objects more strategically? It depends on your requirements as to whether something like that will help you.
I came across an interesting scenario today using StructCopy with the URL scope.
I'm not looking for answers on how to get around it - I know/have used structAppend/duplicate etc
I wanted to copy the URL scope to a new struct so I can process/alter it without effecting the URL scope itself, so I used structCopy( url ). I know that structcopy is a shallow copy of the struct, but as my url scope will only contain url params (as far as I know - is this correct? they are all I am interested in this case anyway) which are string key/values then I figured a shallow copy would be adequate (string being immutable and all).
However, when copying the URL scope, it resulted in strange behaviour - it appears as though structCopy(url) just returns the url scope - not a copy of it, not an error. For example, if I perform the following (assume I have url query params, including one called "rob":
<cfset local.clonedUrl = structCopy( url ) />
<cfdump var="#local.clonedUrl#">
<cfdump var="#url#">
<cfset structDelete( local.clonedUrl, "rob" ) />
<cfdump var="#local.clonedUrl#">
<cfdump var="#url#">
In the above scenario, after the struct delete call, the clonedUrl and URL have both had the entry removed (obviously, performing the above with a normal created Struct rather than URL in the first place behaves correctly)
So, my questions are:
what? I have seen that the URL scope is actually an instance of coldfusions URLScope class, so maybe treated differently for that reason, but returning the same instance from a structCopy call is just mean. At very least if it can't do it I would expect an error, not a result that looks very similar to expected but actually exactly the behaviour I want to avoid. Also noticed that if I pass a component to structCopy it appears to have similar results.
What is the URLScope - does it extend Struct? When I dump the classname I see it is a URLScope, but just dumping the object dumps it like it would a Struct. Anyone got any advice for viewing either the sourcecode for the object or at least an API/Javadocs type spec?
I'm using CF10 (I'm not looking for answers on how to get around it - I know/have used structAppend/duplicate etc)
Thanks for the comments folks - the conclusion was don't use StructCopy() with the URL object (and possibly on the Form scope object etc). It's pretty grim, but URL isn't technically a struct, so all bets are off. (I moan some more here)