validating JSON posted to a CFC - coldfusion

I have a CFC (call it proxy.cfc) that I'm using as a proxy for a simple API that I've written. Everything has been going along just fine and a few partners are starting to use the API effectively.
However, one site that is attempting to post data isn't sending valid JSON and I can't seem tho figure how to gracefully handle this error.
A valid JSON string that is posted as a URL param may look like this:
{"apicomponent":"proxyRemoteAdd","apimethod":"add","apiarguments":{"ph_num":1212,"rbpid":999,"ph_exch":555,"state":"HI","address_1":"123 Main Street","address_2":"","rmtid":"PON83","last_name":"Smith","test":1,"zip":999999,"first_name":"Joe","email":"test#test.com","city":"Honolulu","type":"SP","ph_area":995},"apiauthkey":"abc123"}
And that works just fine.
However, if that string is truncated for any reason:
{"apicomponent":"proxyRemoteAdd","apimethod":"add","apiarguments":{"ph_num":1212,"rbpid":999,"ph_exch":555,"state":"HI"
I catch an exception as follows: Exception: JSON parsing failure: Unexpected end of JSON string
This is coming from my onError in Application.cfc. I did add some code to isolate it in Application.cfc as follows:
<cfif ARGUMENTS.EXCEPTION.MESSAGE IS "JSON parsing failure: Unexpected end of JSON string">
<!--- do some stuff here --->
</cfif>
Is that the best way to handle that sort of error? Is there anyway to pass it off to the proxy.cfc file so I can return an error message back to the posting client? As it is now the Application.cfc is catching it and not even letting me get to the proxy.cfc file.
UPDATE - here's some specific code samples. This is how I've been testing and will illustrate how the data gets posted:
<cfhttp url="https://www.domain.com/api/proxy.cfc" method="post" result="httpResult" charset="UTF-8">
<cfhttpparam type="url" name="method" value="apiauth"/>
<cfhttpparam type="url" name="argumentCollection" value="#jsData#"/>
</cfhttp>
"apiauth" is the method in the CFC that acts as the authorization and proxy.
The argument collection is a JSON string as shown below. It lists a component (a different CFC), the method in that component, the authkey of the user accessing the AP, and then a JSON string called apiarguments which contains the arguments and data that get passed to the apicomponent listed.
The proxy CFC file looks like this:
<cffunction name="apiauth" access="remote" returntype="any" output="false" returnFormat="JSON">
<cfargument name="apicomponent" required="yes" type="string"/>
<cfargument name="apimethod" required="yes" type="string"/>
<cfargument name="apiauthkey" required="yes" type="string"/>
<cfargument name="apiarguments" required="yes" type="struct"/>
<cfset var LOCAL = {}/>
<cfif not isDefined("ARGUMENTS.apiauthkey")>
<cfreturn THIS.NewErrorResponse("Error 401 Malformed Request.") />
</cfif>
<cfif not isDefined("ARGUMENTS.apicomponent")>
<cfreturn THIS.NewErrorResponse("Error 402 Malformed Request.") />
</cfif>
<cfif not isDefined("ARGUMENTS.apimethod")>
<cfreturn THIS.NewErrorResponse("Error 403 Malformed Request.") />
</cfif>
<cfset LOCAL.checkpwResult = FALSE/>
<cfset LOCAL.apicomponent = ARGUMENTS.apicomponent />
<cfset LOCAL.apimethod = ARGUMENTS.apimethod />
At this point some other CFCs are access to check the API key and user ID # to make sure they can access the API. If everything checks out there the data gets passed off to the correct component/method:
<cfinvoke component="#LOCAL.apicomponent#" method="#LOCAL.apimethod#" argumentcollection="#apiarguments#" returnvariable="LOCAL.Response.Data"/>
However the JSON exception is being thrown before this CFC can even be accessed by the Application.cfc file. If I drop a quick cfmail in proxy.cfc to just dump and mail the arguments it doesn't even hit that.
Here's a chunk of the stack trace:
coldfusion.runtime.JSONUtils$JSONParseOverflowException: JSON parsing failure: Unexpected end of JSON string at
coldfusion.runtime.JSONUtils$ParserState.incrementOffset(JSONUtils.java:1999) at coldfusion.runtime.JSONUtils$ParserState.incrementOffset(JSONUtils.java:1980) at coldfusion.runtime.JSONUtils.parseString(JSONUtils.java:1385) at coldfusion.runtime.JSONUtils.parseObject(JSONUtils.java:1074) at coldfusion.runtime.JSONUtils.parseStruct(JSONUtils.java:1178) at coldfusion.runtime.JSONUtils.parseObject(JSONUtils.java:1059) at coldfusion.runtime.JSONUtils.parseStruct(JSONUtils.java:1178) at coldfusion.runtime.JSONUtils.parseObject(JSONUtils.java:1059) at coldfusion.runtime.JSONUtils.parseJSON(JSONUtils.java:1028) at coldfusion.runtime.JSONUtils.deserializeJSON(JSONUtils.java:168) at coldfusion.runtime.JSONUtils.deserializeJSON(JSONUtils.java:128) at coldfusion.filter.FilterUtils.GetArgumentCollection(FilterUtils.java:50) at coldfusion.filter.ComponentFilter.invoke(ComponentFilter.java:193) at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:442) at coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:48) at coldfusion.filter.MonitoringFilter.invoke(MonitoringFilter.java:40)
coldfusion.filter.PathFilter.invoke(PathFilter.java:112) at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:94) at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28) at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38) at coldfusion.filter.NoCacheFilter.invoke(NoCacheFilter.java:58) at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38) at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22) at coldfusion.xml.rpc.CFCServlet.invoke(CFCServlet.java:155) at coldfusion.xml.rpc.CFCServlet.doPost(CFCServlet.java:331) at javax.servlet.http.HttpServlet.service(HttpServlet.java:641) at javax.servlet.http.HttpServlet.service(HttpServlet.java:722) at

If your argument is always a JSON string, you should be able to change the argument type to string, and pass it in that way (it will make it through Application.cfc) and then validate it within your apiAuth function using isJSON() - you should be validating it against malicious JSON anyways, so it won't be much code to initially check against isJSON(). This way, your API will give meaningful feedback and keep all the code where it should be.
However if you have other users passing an actual struct into your method, that won't work.
And you will definitely want to find out why the JSON string is being truncated. At a guess I would suggest there might be some un-escaped characters being passed in which is truncating the string early...

Related

Assign one error log file per application

We have multiple applications running on the same server and default log files end up being a mess of everything, especially the Exception log for which the admin panel does not offer search capacities.
Is it at all possible to have Coldfusion log things pertaining to a given application (as defined by Application.cfm or .cfc) to a separate log?
If not, any alternative solutions to this issue?
I'd recommend application.cfc onError method to log uncaught errors in separate log file. Also this doc could be helpful: Handling errors in Application.cfc
Here's a basic example of using the onError function in application.cfc to log errors to an application-specific log file.
<cfcomponent output="false">
<cfset this.name = "myApp">
<cffunction name="onError">
<cfargument name="exception" required="true">
<cfargument name="eventName" type="string" required="true">
<cfset var myAppName = this.name>
<cfset var details = "">
<!--- You need to specify and check for the exception details you want to include in your log text --->
<cfif IsDefined( "exception.type" )>
<cfset details = "Type: #exception.type#. ">
</cfif>
<cfif IsDefined( "exception.message" )>
<cfset details = details & "Details: #exception.message#">
</cfif>
<cflog type="error" file="#myAppName#" text="#details#">
<!--- Specify how you want the error to be handled once it's been logged --->
<cfdump var="#exception#">
</cffunction>
</cfcomponent>
You need to specify which parts of the exception details you want to include in your log entry, bearing in mind that the keys of the exception struct will vary according to the type of error thrown. It's therefore best to check for their existence before adding them to the log text.
Official onError docs for ColdFusion MX7 (since your question's tagged with that version - which is also why I've used tags rather than cfscript in my example to be on the safe side).

how to check if a file exists on Amazon S3 using Coldfusion fileExist?

I'm running Coldfusion8 and am uploading files to Amazon S3.
When displaying images, I want to check whether an image is available from S3 and if not show a fallback image. My problem is, don't know how to check for existing images.
If I list the link to an image, it's something like this:
http://s3.amazonaws.com/bucket/l_138a.jpg?AWSAccessKeyId=_key_&Expires=_exp_&Signature=_signature_
I'm trying to check for existing files like this:
<cfif fileExists("http://s3.amazonaws.com/bucket/s_" & items.filename)>
<cfdump output="e:\website\test\dump.txt" label="catch" var="found!!!">
</cfif>
Question:
Do I always have to provide accesskey, expires and signature when checking for an image? If I enter the image path without credentials in the browser, the image is loaded, so I don't understand why my fileExist is not working. Any idea?
You could use cfhttp if you have a site-wide page not found message set up.
<cfhttp url="http://a.espncdn.com/photo/2012/0813/nfl_u_flynn1x_203.jpg" method="head">
<cfdump var="#cfhttp.filecontent#">
returns object of java.io.ByteArrayOutputStream
<cfhttp url="http://a.espncdn.com/photo/20notanimage3.jpg" method="head">
<cfdump var="#cfhttp.filecontent#">
returns <html> <body> <h1>Error Processing Request</h1> </body> </html>
Can also check the statuscode returned by the server
<cfhttp url="http://a.file.exists.gif" method="head">
<cfdump var="#val(cfhttp.statuscode)#">
200 is ok, 404 is not found, etc
I've used the getObjectInfo method in the S3.cfc to see if an object exists:
<cffunction name="getObjectInfo" access="public" output="false" returntype="string"
description="Creates a bucket.">
<cfargument name="bucketName" type="string" required="yes">
<cfargument name="filekey" type="string" required="true" hint="" />
<cfset var data = "">
<cfset var content = "">
<cfset var contents = "">
<cfset var thisContent = "">
<cfset var allContents = "">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<!--- Create a canonical string to send --->
<cfset var cs = "HEAD\n\n\n#dateTimeString#\n/#arguments.bucketName#/#Arguments.filekey#">
<!--- Create a proper signature --->
<cfset var signature = createSignature(cs)>
<!--- get the bucket via REST --->
<cfhttp method="HEAD" url="http://s3.amazonaws.com/#arguments.bucketName#/#Arguments.filekey#">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
</cfhttp>
<cfreturn cfhttp.StatusCode />
</cffunction>
If I get a 200 status back, then I know the object exists.
I haven't used Coldfusion for a long time, but I did a quick lookup and the fileExists method seems to be for filesystem lookups, not remote URLs.
There are other Coldfusion methods for requesting URLs. One forum discussion on the subject I just quickly found is here: http://forums.adobe.com/thread/765614
But, assuming you're generating HTML to be consumed by a web browser I would suggest doing an image check / fallback in HTML/CSS/JS rather than server side. You could do this with CSS background-image tricks, or directly load and check images with JS. One question dealing with this that I found is here (there are probably a bunch of similar questions on this stuff): Inputting a default image in case the src attribute of an html <img> is not valid?
CF9 +
<cfscript>
FileExists('s3://#accessKey#:#secretKey##[your bucket]/[your file]');
</cfscript>

Catching errors database errors in ColdFusion

I have a ColdFusion cfm file that communicates with a sql server database. Right now if something goes wrong with the database connection, it brings up an error page generated by ColdFusion. Is there a way I can catch the errors and display a message like, "Database server temporarily down, please come back later"?
Ted
You can use try/catch for an individual query - this will be the most granular approach.
<cftry>
<cfquery datasource="myDSN">BROKEN SQL</cfquery>
<cfcatch type="database">
<h1>Database Offline!</h1>
<p>Sorry, the database threw an error: #cfcatch.queryError#. Try again later.</p><cfabort>
</cfcatch>
</cftry>
You can also use the cferror tag for global exception handling (put this in Application.cfm):
<cferror
type="exception"
exception="database"
template="myFriendlyDatabaseErrorTemplate.cfm">
You can also use onError method within Application.cfc, which will also (like the cferror tag) catch all errors that occur during the request:
<cffunction name="onError" returnType="void">
<cfargument name="Exception" required=true/>
<cfargument name="EventName" type="String" required=true/>
<cfif arguments.Exception IS "database">
<cfinclude template="myFriendlyDatabaseErrorTemplate.cfm">
</cfif>
</cffunction>

ColdFusion onSessionEnd problem

I have read many posts by people who have problems with onSessionEnd. This is my first conversion of application.cfm to application.cfc and the onSessionEnd is not working with the CFFunction I am trying to invoke.
I guess what's hanging this up is how to properly call the component from the /lib/components/ folder where it resides.
When a user logs in I am creating a session array that tracks a jobNumber and the last_completed_step in that job. There are multiple jobs in a users session. At the end of the session I want to write the updated array data back to the DB.
I should make it clear that at present I look into my log file and see that the session is started - as coded in the onSessionStart shown below. Furthermore, the onSessionEnd also writes to the log file when I take out the invocation of the component. In other words if I just tell it to write "Session ended." to the log file I will see it in the log file. I have set current session timeout in CF Administrator and my app.cfc for 3 minutes for testing.
If I call the "giveMeAnswer" method in the jobState.cfc from a separate file (also at the root level) the giveMeAnswer method works properly and returns the value "I am a CFC."
If I move the jobState.cfc to the root level and set the component attribute to "jobState" I am also getting a return from the component.
<!--- Runs when your session starts --->
<cffunction name="onSessionStart" returnType="void" output="false">
<!--- :: invoke all session variables | moved out of on session start :: --->
<cfinvoke component="#application.virtualPaths.cfcPath#system/sessionVars" method="init" />
<cflog file="#This.Name#" type="Information" text="Session started.">
</cffunction>
<!--- Runs when session times out --->
<cffunction name="onSessionEnd" returntype="void">
<cfargument name="SessionScope" type="struct" required="true" />
<cfargument name="ApplicationScope" type="struct" required="true" />
<cfinvoke component="/lib/components/jobState" method="giveMeAnswer" returnvariable="returnFromCfc">
</cfinvoke>
<cflog file="#This.Name#" type="Information" text="Session ended. #returnFromCfc#">
<cfreturn />
</cffunction>
So, is it just not finding the component? Any other ideas?
Thanks much, Jerry
I know I've seen folks use / in component calls before, but I do not believe it is officially supported. You want to use a dot notation path instead, ala
component="lib.components.jobstate"
and assure that lib is either a subdirectory or a known CF mapping that points to the lib folder.

Coldfusion: splitting a tag across onRequestStart() and onRequestEnd() in Application.cfc

I'm trying to see if there is a way to split a CFSAVECONTENT tag across the onRequestStart() and onRequestEnd() functions in Application.cfc to save the generated HTML of any .cfm page in the application to a variable.
Adding <cfsavecontent variable="html"> to onRequestStart() and adding </cfsavecontent> to onRequestEnd() isn't allowed since the tag must be closed in the function.
Is this even possible to do? I'm trying to avoid hard coding the CFSAVECONTENT this into every .cfm page of the site.
Thanks!
Alex,
You could do something like this in OnRequest (untested, but should work).
<cffunction name="onRequest" returnType="void">
<cfargument name="thePage" type="string" required="true">
<cfsavecontent variable="html">
<cfinclude template="#arguments.thePage#">
</cfsavecontent>
<!--- do whatever you want with the html variable here (for example, output it) --->
<cfoutput>#html#</cfoutput>
</cffunction>
I realize this has an accepted answer already, but another way to accomplish this without using cfinclude would be to use the getPageContext() object in onRequestEnd() to nab the generated content:
<cffunction name="onRequestEnd" output="yes">
<cfargument type="string" name="targetPage" required="true" />
<cfset var html = getPageContext().getOut().getString() />
<!--- Manipulate the html variable. --->
<cfoutput>#html#</cfoutput><cfabort />
</cffunction>
The <cfabort /> is important here because if you don't abort the request, the CF engine will output the generated content again and it will end up sending two copies of the output along.
I've used this method to apply site-wide changes to content on sites in a crunch where finding every instance of the original content wasn't practical or timely enough. It can also be used to send the generated content out to a translation service if needed before being returned to the end-user.