Is there an easy way to split a comma delimited string into an array using cfscript?
Something similar to the following JavaScript:
var a = "a,b,c".split(",");
var a = ListToArray("a,b,c,d,e,f");
https://cfdocs.org/listtoarray
Your two main options are listToArray(myList) and the java method myList.split(), as noted in previous answers and comments. There are some things to note though.
By default ColdFusion list functions ignore empty list items.
As of ColdFusion version 8, listToArray takes an optional third argument, includeEmptyFields, which is a boolean controlling that behavior, defaulting to false.
For example:
listToArray("asdf,,,qwer,tyui") is ["asdf", "qwer", "tyui"]
listToArray("asdf,,,qwer,tyui", ",", true) is ["asdf", "", "", "qwer", "tyui"]
Re java split:
Like other java functionality that pokes up through the ColdFusion layer, this is undocumented and unsupported
In Adobe ColdFusion 8, 9, and 10, but not in Railo, this is a syntax error:
a = "asdf,,,qwer,tyui".split(",")
But this works:
s = "asdf,,,qwer,tyui";
a = s.split(",");
As far as I can see, Adobe ColdFusion treats the result of .split() like a ColdFusion array:
CFDumps show it as an array
It's 1-based
You can use arrayLen on it
You can modify its elements in ColdFusion
There may be other behaviors I didn't check that aren't like a CF array, but as above, it's unsupported
In Railo:
Debug dumps show it as Native Array (java.lang.String[])
The other statements about its very array-like behavior are still true
That's in contrast to real java arrays, created with createObject("java", "java.util.ArrayList").
NOTE: That's only partly correct; see edit below.
For instance, in Adobe ColdFusion, elements of a java ArrayList can't be modified directly with CFML
In general, Railo handles java arrays more like ColdFusion ones than ACF
Edit: Thanks Leigh, I stand corrected, I should stick to what I know, which is CF way more than java.
I was reacting to the comment saying that the result of .split() "is not a ColdFusion array, but a native Java array. You won't be able to modify it via CF", which is not the case in my experience. My attempt to clarify that by being more specific was ill-informed and unnecessary.
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 have adopted the CFScript syntax for most of the work with ColdFusion now, since with the new version of ColdFusion v11(codenamed Splender), almost all the short-comings of the script style syntax has been given serious thoughts. Thought suprisingly, I came across a requirement where I needed to iterate throught the list with variable delimiter. So I choose the list.each function in CF11 and not any other option would do since I also need the current index value along the way as well.
list.each(function(element,index,list){
writeOutput("#index#:#element#;");
}, ";")
The problem is that this function surprisingly does not seem to support a custom delimiter.
To save the time, I would like to mention that I already tried the for (element in...) with a count variable for my needs.
var idx=1;
for (element in "a,b,c,d,e"){
writeOutput(element);
LOCAL.idx++;
}
But I would appreciate some help with the original list.each function in CF11, is it even possible somehow to implement? or is it what I think a shortcoming.
I'm not using CF11, but i would point you to this bug report, which seems to say the HF3 does exactly what you want.
If that doesn't work, or in the meantime, you could convert it to an array and use ArrayEach().
The legacy web app I have inherited, which was custom-written for Oxfam New Zealand in classic ASP, runs a string replace on user-submitted inputs removing the string 'cast' presumably because of the cast function.
However this means that none of our participants can have a name or email address that contains that string. This is causing problems for someone with the surname Hardcastle.
This seems completely over the top security-wise - or at least there must be a way to ensure the user inputs are safe without changing the inputs of people with 'cast' in their name or email address.
The actual replace is done with:
strString = (Replace(strString, "cast", "", 1, -1, vbTextCompare))
I'm considering just commenting that line out, would that be safe to do?
The legacy app is doing it wrong.
Rather than filtering the content at the source, the content should be property encoded wherever it is used. In other words, if it's being used in a query, the value would be encoded prior to adding it to the SQL statement or better yet placed unmolested into a stored procedure parameter.
So yes, you can remove that code, but make sure strString is being used safely elsewhere.
I could change it to
strString = (Replace(strString, "cast(", "", 1, -1, vbTextCompare))
that way, you still get the "safety" of escaping the SQL, but won't aggravate users with cast in their names
This is actually an extremely ineffective way to prevent SQL injection attacks. There are 100 other words you would need to replace. The accepted answer is incorrect in that this does not retain the safety of this code, because there is no safety to begin with. The line with the "(" character would never execute if the hacker just added a space between CAST and (.
See this question on parameterized queries in classic asp. You never want to concatenate user supplied data to make a sql string. There are ways to do this without concatenation that are correct, but the code you currently have is useless. You might as well just remove that line.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 11 years ago.
In the spirit of my other questions regarding "common programming mistakes ... to avoid"
What are some common programming mistakes for a ColdFusion programmer to avoid?
set <cffile> upload path to a web accessible, CF-enabled directory!!!
isStruct() before isObject() in a series of <cfif>'s expecting isStruct only catches struct (cfc component returns True from isStruct() as well)
no HtmlEditFormat() when displaying user-generated content (XSS)
forgot to add output=false on CFC methods
not using <cfqueryparam> inside <cfquery>
not scoping not-so-evident variables like cfquery name or loop index in a method
use <cfform> when all they need is plain-vanilla HTML <form>
forgot to UrlEncodedFormat() user-defined URL
use <cffeed> without sanitizing the content
trust isDate() too much (any number would return true)
expect string comparison to be case-sensitive (IS and EQ operators are case-insensitive)
sending strings "yes" or "no" to SerializeJSON() without appending a whitespace to preserve the string (otherwise SerializeJSON() or DeserializeJSON() will translate them to "true" and "false")
not putting singletons services in application scope
blindly create as much CFCs as one wants like one would do in JAVA
putting complex value/object into a list (can't, list is just a string of comma-seperated values)
writing functions that takes array as an argument and modify that array expecting that array will be modified (array in CFML is passed by value)
blindly changes access="remote" on a method and expect it to work (when remote proxy is generally more appropriate)
use a lot of WriteOutput() in cfscript when CFML is more appropriate
blindly uses IsDefined() when StructKeyExists() can generally do it more efficiently
blindly uses Iif() and De() without knowing they're as nasty as Evaluate()
update some code in onApplicationStart() and not seeing the difference on refresh (restart the app!)
<cfloop> or '' outside of <cfquery> causing multiple new query connections to be opened. 99% of the time it's better to have multiple statements inside of one cfquery to perform multiple actions, or to UNION data together.
hardcoding absolute path when ExpandPath() is generally better
forgot to turn on Unicode support in DSN (Unicode becomes '????')
not upgrading to the latest JRE and Hotfixes
misusing Client scope and blow up Windows registry...
uses depreciated/obsolete functions/features (i.e. flash form aka flex 1.x alpha, cftable, Verity full-text search, etc...)
passing CFCATCH to a function as argument type Struct (CFCATCH behaves like a Struct, but it is not. Just pass it as type 'Any').
Not reading CFC Best Practices from ColdBox wiki.
buying in the mindset of .ASP(X) or .JSP or [insert web technology] are always better.. ;)
not use PrecisionEvaluate() and getting all sort of floating point rounding error especially when calculating money.
Inappropriate use of #
SELECT *
Not scrubbing URL/form inputs
Debugging on in production environment (even if output is suppressed)
Shamelessly stealing Henry's formatting...
it is faster and more accurate to check explicit boolean instead of implied; use <cfif query.recordCount GT 0> instead of <cfif query.recordCount>
do not use evaluate(), de(), or iif()... ever. there is always a way around these slow functions
understand structures, keys, values and how to access query and structure data using array notation. (this will usually get around your need for evaluate())
do not use pound signs unless you are outputting data or otherwise creating a string (don't do this: myFunction(arg = #myVar#) )
read and understand the difference between THIS and VARIABLES scope in a CFC
avoid extreme overuse of <cfsilent> when you probably need to use a <cfcontent reset="true"> just before you begin your output (before doctype, xml declaration, or <html>)
don't blindly drop ColdFusion values into an HTML script block (javascript) without using jsStringFormat()
if you are not using <CDATA> text in your XML, you may want to use xmlFormat() when creating an XML document
don't use Windows registry for Client scope data. Use the database.
if your IT architecture permits, use Session data instead of Client data.
use <cflock> correctly and consistently; shared data will leak in your Application.
if you are going to use Java objects, understand the Java error messages (for example, 'method cannot be found' may not mean the method does not exist at all, it means the method does not exist for the arguments you've supplied)
if you have to read large files, either use the new CF8 "File" functions or hand off the task to Java on CF6 & 7. <cffile> is inefficient for large files.
understand pass-by-reference and pass-by-value, and how those concepts work in CF; specifically when using functions to modify XML documents
as Henry stated, always use <cfqueryparam>; also ensure that you are using the correct CFSQLType parameter for your DBMS (for date, time, timestamp, etc)
don't chain together a series of <cfif> and <cfelseif> logic blocks, use <cfswitch> and <cfcase> if you have more than three conditions you need to handle
more of an architecture note: always do some sort of server side validation to catch the nasty data the wolf-shirt-wearing user may be passing you
last architecture note: let CF do your middle layer of data retrieval and display and let your webserver do webserver things like SEO URLs (I'm looking at you ColdCourse)
SQL Injection Attacks. It seems like cfquery is just made to allow them. So you should use cfqueryparams.
In Coldfusion, all variables are global by default, unless they are declared with the var keyword. (Somewhat similar to the situation in Javascript.)
So you either have to remember to var every variable used in a function, including things like names that are using in a cfquery name, or you can just use this trick:
<cffunction name="MyFunction">
<cfset var Local = StructNew()>
<!--- Now anything Local. is automatically local --->
<cfset Local.x = 42>
<!--- Including cfquery name="" --->
<cfquery name="Local.Customers" datasource="some_datasource">
SELECT C.ID, C.Name
FROM Customers C
</cfquery>
</cffunction>
There's nothing magic about the name Local, it's just convention. Although Coldfusion 9 will add an explict local scope, so if you use Local it will probably ease upgrading to CF9 when the time comes.
Note that the situation is a tad different for CFCs: In CFCs, the variables scope (the "default" scope) isn't global like it is for normal functions but rather exists per instance of your CFC. So while forgetting to use var is not quite as dangerous in a CFC as it is in a top-level function, the best practice is still to use var all the time.
Failing to prevent users from seeing coldfusion errors.
Add a onError method to a top level Application.cfc to prevent users from seeing those all to detailed dump messages exposing your inner workings(and failings).
<cffunction name="onError" returntype="void" output="true">
<cfargument name="exception" type="any" required="true" />
<cfargument name="eventname" type="string" required="true" />
varscoper is also a great tool for automating the check for variable scoping omissions in components.
http://varscoper.riaforge.org/
Overuse of 'query of query'. That is, further filtering or sorting of query results using the cfquery tag.
This type of work is often done better by the database itself, particularly if the dataset is large.
One of the biggest mistakes would be not using cfqueryparam
Very bad:
SELECT UserName
FROM Customers
WHERE CustomerID = #URL.custid#
Very good:
SELECT UserName
FROM Customers
WHERE CustomerID = <cfqueryparam value="#URL.custid#" cfsqltype="cf_sql_integer">`
Making that mistake will cost you a website.
Putting variables in the wrong scope; even if you don't blow up the registry or crash the server, it's easy to slowly drain performance from your application by bumping variables up to the highest scope in which you think you might need them, or to lose information because you stored it in one scope and tried to access them in a different scope.
Using cfcatch without capturing and/or transmitting some information about the error so that it can be found and fixed. (It's difficult to find an error that doesn't tell you it occurred.)
Using listcontains() when you want listfind(). Especially if the list contains numbers. listfind() matches only an entire item in a list; listcontains() matches part of an item. (Yes, we made this mistake once.)
With administrator access:
Leaving the defaults for a data source set up on the server. "Least privileges" applies on the CF side as well; don't give it any more permissions than it specifically needs. (GRANT, ALTER, REVOKE, DROP ... you don't really want those checked.)
Not checking the boxes for retrieving all the contents from a CLOB/BLOB field when that's what you're expecting. (It was really interesting seeing that applied to a field in which we were storing PDFs.)