ColdFusion having multiple cfqueries in cffunction - coldfusion

I am trying to write a function for a survey where it pulls questions from a database. The catch is that there are both active and unactive questions. I need older questions to show up when someone views the results from an old survey.
Here is the code I am trying within a CFC:
<cffunction name="getAllQuestions" access="public" returntype="query">
<cfargument name="survey" default=0>
<cfif len(#survey#) gt 0>
<cfquery name="getsdate" datasource="blah.database">
select * from past_survey
where survey_id = #survey#
</cfquery>
<cfreturn getsdate>
</cfif>
<cfquery name="getquestions" datasource="blah.database">
select * from pool_questions
<cfif len(#survey#) eq 0>
where active_flag='Y'
<cfelse>
where <cfqueryparam value="#dateformat
(getsdate.survey_date, "yyyy/mm/dd")#"> BETWEEN start_date AND
end_date
</cfif>
order by qtn_nb
</cfquery>
<cfreturn getquestions>
</cffunction>
#survey# is the survey id which is generated by the form. What I am trying to do is that if survey has a value to run query getsdate. Then the second query would run no matter if survey has a value or not. If there is not value it should pull all active questions. If there is a value then it should check if the survey date is between the start date and end date for past questions.
Any advice on how to make this work would be greatly appreciated. Thank you in advance!

<cffunction name="getAllQuestions" access="public" returntype="struct">
<cfargument name="survey" required="true" default="0" type="numeric">
<cfset var qryReturn = ""> <!---Always var scope your variables to prevent them from leaking to other functions --->
<cfset var structReturn = structNew()>
<cfset structReturn.pastSurvey = "">
<cfset structReturn.surveyQuestions = "">
<cfif survey GT 0>
<cfquery name="qryReturn" datasource="blah.database">
SELECT *
FROM past_survey
<!--- Always cfqueryparam to prevent SQL injection attacks & also always reference the scope to prevent confusion --->
WHERE survey_id = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.survey#">
</cfquery>
<cfset structReturn.pastSurvey = qryReturn>
<cfelse>
<cfquery name="qryReturn" datasource="blah.database">
SELECT *
FROM pool_questions
<cfif arguments.survey EQ 0>
WHERE active_flag = 'Y'
<cfelse>
WHERE <cfqueryparam value="#dateformat
(getsdate.survey_date, "yyyy/mm/dd")#"> BETWEEN start_date AND
end_date
</cfif>
ORDER BY qtn_nb
</cfquery>
<cfset structReturn.surveyQuestions = qryReturn>
</cfif>
<cfreturn structReturn>
</cffunction>
You probably should be doing this in two separate functions, but I will attempt to answer your question.
My code will return a struct of queries (you can change to an array if you prefer) that returns a past survey and the survey questions
Note: In your example code, you have a few bad practices.
You are checking the length of the survey value rather than checking the value itself.
If you want to ensure that survey always has a value regardless of if it is passed or not, set requried=true and give it a default value.
Use cfqueryparam to prevent sql injection attacks
Any variables created in the function need to be var scoped to prevent them from leaking to other cffunctions in the same cfcomponent. I always do this at the top. Yes, even the name you give a cfquery needs to be var scoped.
Since you are doing a return after your first query, if the survey value is greater than 0 it will never get to the second query where it has the date check.

I see the following problems you need to address.
First, your survey argument has a default value of 0 and you are doing conditional logic on the length of it. The length of "0" is 1, so that condition will always return true.
Next, you state that you want the 2nd query to run whether the first one runs or not, but you refer to a value from the 1st query in the 2nd one. That means if the 1st query does not run, the 2nd one will crash due to an undefined variable.
Next, dateformat returns a string. Applying it the way you do in the 2nd query is at best unnecessary, and at worse, and indication that you are storing the start and end dates in pool_questions as strings. If you are attempting to strip out the time portion of the datefield in the first query, ColdFusion has a cast() function for that.
Also, scope your variables. ie - instead of <cfif len(survey), do this <cfif len(arguments.survey).
Also, var your local variables. In this case, it's the names of your two queries.
That should get you started.

Related

How to counter this SQL Injection risk (Coldfusion)?

Recently, a security risk company we hired identified a SQL injection risk for a specific parameter of a function in our Coldfusion application. The argument is correctly captured as a cfargument:
<cfargument name="status" required="false" type="string">
and also correctly paramed as a cfprocparam:
<cfif isDefined('ARGUMENTS.status') AND ARGUMENTS.status NEQ "">
<cfprocparam type="in" value="#ARGUMENTS.status#" dbvarname="#search_status" cfsqltype="cf_sql_varchar">
<cfelse>
<cfprocparam type="in" null="yes" dbvarname="#search_status" cfsqltype="cf_sql_varchar">
</cfif>
And then it's used in the stored proc this way, building a where clause for a SQL query:
if (len(#search_status) > 0)
begin
set #strWhere = #strWhere + ' AND e1.cps_txt LIKE '+ '''%'+#search_status+'%'''
end
Of course, paraming to make sure the value is a varchar can't prevent a string of malicious code being entered and passed. This seems like it could easily be a larger problem, too, since it's far from the only place in our application where string arguments are passed to cfc functions and then used in SQL queries.
I'm wondering if the best approach to remedy the security risk in this one case is to compare the argument's value to all of the possible expected values (there's about twenty of them, stored in a table), and if it doesn't match one of them, then to throw an error, or just pass to the stored proc as null.
Had to tackle this at a previous employer. The trick is to pass true or false to the null argument and to avoid writing all this conditional code that calls different versions of the procparam with or without the attribute. Just always have the attribute and put the conditional logic in the value.
<cfprocparam type="in"
dbvarname="#search_status"
cfsqltype="cf_sql_varchar"
value="#ARGUMENTS.status#"
null="#!(structKeyExists(arguments, "status") AND (len(arguments.status) NEQ 0))#">
Depending on the complexity of the condition, you can simply pass it as a variable.
<cfset statusNull = true>
<cfif structKeyExists(arguments, "status") AND (len(arguments.status) NEQ 0)>
<cfset statusNull = false>
</cfif>
<cfprocparam type="in"
dbvarname="#search_status"
cfsqltype="cf_sql_varchar"
value="#ARGUMENTS.status#"
null="#statusNull#">
And I'd like to reiterate the comment from SOS that mentions dynamic SQL. We also eliminated dynamic SQL from thousands of stored procedures and replaced it with the conditional check from his linked message. This is so much easier to read and debug than dealing with acres of string concatenation code.
WHERE ( #OrderId IS NULL OR tblOrder.OrderId = #OrderId )
AND ( #OrderCustomer IS NULL OR tblOrder.OrderCustomer = #OrderCustomer )
I would also avoid adding a query just to look up valid values for status before passing the argument to the this function/proc. That just adds overhead to you system for every call to this process.
I think you pretty much answered your own question. Here's an approach I've used in the past to handle validations using accepted values stored in a small lookup table. Do this before calling your stored procedure. I'll use generic names since I don't know your table and column names.
<!--- Validation code prior to running stored proc --->
<cfif isDefined('ARGUMENTS.status') AND ARGUMENTS.status NEQ "">
<!--- Query acceptable values --->
<cfquery name="qValidateSearchStatus" datasource="myDsn">
select
search_status
from
lookup_table
</cfquery>
<cfif listFind(valueList(qValidateSearchStatus.search_status), ARGUMENTS.status)>
<cfset statusValid = true>
<cfset statusNull = false>
<cfelse>
<cfset statusValid = false>
<cfset statusNull = false>
</cfif>
<cfelse>
<cfset statusValid = true>
<cfset statusNull = true>
</cfif>
<cfif statusValid>
<!--- Call stored procedure --->
<cfstoredproc procedure="myStoredProc" datasource="myDsn">
<cfif statusNull>
<cfprocparam type="in" null="yes" dbvarname="#search_status" cfsqltype="cf_sql_varchar">
<cfelse>
<cfprocparam type="in" value="#ARGUMENTS.status#" dbvarname="#search_status" cfsqltype="cf_sql_varchar">
</cfif>
</cfstoredproc>
<cfelse>
<!--- Process error handling --->
</cfif>

Can not find the difference between accessing property in a query with '[bracket]' and '. dot' notation

I have a code snippet where I am fetching rows from a database and finding dateDiff from one of the columns in the query.
<cfquery name="querySearchUUID" maxrows="1">
SELECT [DateInvited]
FROM [INVITE_PERSON]
WHERE [UUID] =
<cfqueryparam value="#arguments.userUUID#"cfsqltype="cf_sql_varchar">
</cfquery>
<cfif querySearchUUID.RecordCount EQ 1>
<cfif dateDiff('h', querySearchUUID.DateInvited, now()) LTE 24>
<cfreturn true>
<cfelse>
<cfreturn false>
</cfif>
<cfelse>
<cfreturn false>
</cfif>
However when I change the code querySearchUUID.DateInvited to querySearchUUID['DateInvited'] in the dateDiff() It fails I dont know why.
Here was the error it produced.
The value class coldfusion.sql.QueryColumn cannot be converted to a date.
Can anyone please explain me what exactly is the difference.
Thank you.
According to my experience, it depends on the way the variable is used. When you use it in a cfdump tag some ColdFusion magic prints the value from the cell in the first row. When you pass it into a function, the original object of type coldfusion.sql.QueryColumn is passed. You can always find ot the type calling getMetadata like <cfdump var="#getMetadata(querySearchUUID['DateInvited'])#">
When you need to pass a value you have to use the line number querySearchUUID['DateInvited'][ 1 ] starting with one for the first row.

conditional query in coldfusion

I need to provide some status on items in my table which I do in the last column of my table.
First I go and query one table to see if I have a confirmation for the item .
<cfquery name="focnotice" datasource="******" result="FocResult">
SELECT ecspc
FROM tbl_CNR_H
WHERE icsc = '#myarray[i].ICSC#'
AND asr_no = '#myarray[i].ASR#'
</cfquery>
The ECSPC is a field in my Table, so logic is see if there is a record. If so, see if the ECSPC value is something other then "". If so, query another table to see if there is a matching record for this ECSPC.
<cfset ISUPStatus = "#focnotice.ecspc#">
<cfif ISUPStatus NEQ "">
<cfquery name="isupStatus" datasource="******" result="ISUPResult">
select *
from tbl_ISUP
where dpc = '#ISUPStatus#'
</cfquery>
<cfset isupcount = #ISUPResult.RecordCount#>
<cfif #isupcount# GT 0>
<cfset ISUPorder = "Yes">
<cfelse>
<cfset ISUPorder = "No">
</cfif>
<cfelse>
<cfset ISUPorder = "No">
</cfif>
I get the following error in my debug
Complex object types cannot be converted to simple values.
The expression has requested a variable or an intermediate expression
result as a simple value. However, the result cannot be converted to a
simple value. Simple values are strings, numbers, boolean values, and
date/time values. Queries, arrays, and COM objects are examples of
complex values. The most likely cause of the error is that you tried
to use a complex value as a simple one. For example, you tried to use
a query variable in a cfif tag.
What am I missing here ?
You are passing invalid parameter into the Query "myarray[i].ICSC",'#myarray[i].ASR#'. You need to specify what index of array you are using.
<cfquery name="focnotice" datasource="*******" result="FocResult">
Select ecspc
From tbl_CNR_H
Where icsc = <cfqueryparam cfsqltype="cf_sql_varchar" value="#myarray[1].ICSC#">
AND
asr_no = <cfqueryparam cfsqltype="cf_sql_varchar" value="#myarray[1].ASR#">
</cfquery>
I believe the error causing you the issue lies in:
<cfset isupcount = #ISUPResult.RecordCount#>
From a quick look of your code, try using instead:
<cfset isUpCount = isUpStatus.recordCount>
But in addition please look at the comments above, especially joins.

Coldfusion OutOfMemoryError (CF9/Wheels)

I have a function which loops over a query and updates a database row for each item. After about 7000 iterations it's throwing an out of memory error - Java heap space.
Is there anything obviously wrong with this code ?
<cfloop query=loc.fixItems>
<cfset loc.count = loc.count + 1>
<cfset var categoryName = loc.fixItems.categoryName>
<cfinvoke component="Item" method="updateCode"
itemId="#loc.fixItems.itemId#" code="#loc.fixItems.newCode#"/>
<!--- Increment counter for category --->
<cfif structKeyExists(categoryMap, categoryName)>
<cfset var inc = structFind(categoryMap, categoryName) + 1>
<cfset structUpdate(categoryMap, categoryName, inc)>
<cfelse>
<cfset structInsert(categoryMap, categoryName, 1)>
</cfif>
</cfloop>
and in the update component:
<cffunction name="updateCode">
<cfargument name="itemId" type="numeric" required="yes">
<cfargument name="code" type="string" required="yes">
<cfset var loc = {}>
<cfquery name="loc.update">
update items
set code = <cfqueryparam value="#code#">
where id = <cfqueryparam value="#itemId#">
</cfquery>
</cffunction>
Don't use cfinvoke to create your Item component every iteration of your fixItems query. Create it once before that using createObject and simply call the updateCode method each time directly on the object.
The following can be done:
Change your <cfqueryparam> to use the appropriate cf_sql type. Are code and id really strings?
Don't give your <cfquery> a name. You are not keeping the result anyway. var loc doesn't help either
Bump up you memory to the JVM Addtional approach Use Java 7 and G1GC
Every 100 to 1000 iterations do a forced Garbage Collect
Update your data in bulk. XML based table variables can do this.
Make your function silent
Consider ORM on this

Set a variable dynamically into a structure via CFLOOP

I am trying to set a variable dynamically into a structure via CFLOOP. I have read Ben Nadal's blog post but cant seem to get the assignment correct. I would like to use dot notation and make the VIN a sub structure of values.
Here is my code:
<cfloop query="VINs">
<cfquery name="carsQue" datasource="carsData">
SELECT VIN, MODEL, MAKE, etc
FROM CarsDB
WHERE (VIN = #VIN#)
</cfquery>
<cfset carsStruct= StructNew()>
<cfset carsStruct.[VIN].MAKE = '#carsQue.MODEL#'>
<cfset carsStruct.[VIN].MODEL = '#carsQue.MAKE#'>
</cfloop>
Any guidance would be greatly appreciated,
Thanks
Running a query inside a loop is almost always a bad idea. In your case, a better option would be:
<cfif ListLen(valuelist(vins.vin)) gt 0>
<cfquery name=CarsQue datasource = "carsData">
select vin, model, make, etc
from carsDB
where vin in (<cfqueryparam cfsqltype="cf_sql_varchar"
value="#valuelist(vins.vin)#" list="true">)
</cfquery>
<cfset carsStruct = StructNew()>
<cfloop query="carsQue">
code for your struct
</cfloop>
<cfelse>
code for vins query returning no data
</cfif>
Better yet would be to get all the data with one query. You didn't provide enough information to determine if this was possible, but it often is.
Create a structure outside loop and and setting variable within loop can solve the problem. in a current scenario each time loop run its create a new structure.
you can do some thing like this
<cfset carsStruct= StructNew()>
<cfloop query="VINs">
<cfquery name="carsQue" datasource="carsData">
SELECT VIN, MODEL, MAKE, etc
FROM CarsDB
WHERE VIN = <cfqueryparam cf_sql_type="cf_sql_varchar" value="#VINs.VIN#">
</cfquery>
<cfset carsStruct[VINs.VIN].MAKE = carsQue.MODEL>
<cfset carsStruct[VINs.VIN].MODEL = carsQue.MAKE>
</cfloop>
Based on the limited information you've given you should be able to run one query and loop through that to add to your struct.
<cfset carsStruct= {}> //new struct
<cfif VINs.RecordCount> //your VINs query has records
<cfquery name="carsQueue" datasource="carsData">
SELECT VIN, MODEL, MAKE, etc
FROM CarsDB
// Quoted list of all your VINs. cfqueryparam prevents against SQL injection
WHERE VIN IN (<cfqueryparam cf_sql_type="cf_sql_varchar" value="#ValueList(VINs.VIN)#" list="true">
</cfquery>
<cfloop query="carsQueue">
<cfset carsStruct.[carsQueue.VIN].MAKE = carsQueue.MODEL>
<cfset carsStruct.[carsQueue.VIN].MODEL = carsQueue.MAKE>
</cfloop>
<cfelse>
// if VINs query return nothing a blank struct will be returned.
//You do NOT need this <cfelse> unless you are returning something when the query is blank
</cfif>