title: How to implement the OWASP ESAPI validator with groups of validation attempts in ColdFusion?
body: <p>I have been playing around with the OWASP ESAPI utilities that are included with ColdFusion 9. <a href="http://www.petefreitag.com/item/788.cfm" rel="nofollow">ColdFusion's Builtin Enterprise Security API</a>. The <code>encoder</code> utilities are pretty straight forward and I believe I have them working fine. My problem is with the <code>validator</code> utilities.</p>
  
  <p>I can get them to work <em>singly</em>. That is, if I call the <code>validator.getValidInput()</code> method with "invalid" data it will throw an error that I can catch. However, when I try to call the <code>validator</code> method in <em>batch</em> I get a null pointer exception. By <em>batch</em> I mean attempting to execute groups of validation attempts. This is supposed to work by passing the <code>validator.getValidInput()</code> method a <code>ValidationErrorList</code> parameter which should tell it NOT to throw an error but instead just add the error to the error list. I cannot get it to work in this mode. My best attempt is giving me a null pointer exception.</p>
  
  <p>Here is the specific error:</p>
  
  <pre><code>java.lang.NullPointerException
  </code></pre>
  
  <p>With this stack trace:</p>
  
  <pre><code>java.lang.NullPointerException at 
  org.owasp.esapi.reference.DefaultValidator.getValidInput(DefaultValidator.java:238) at 
  sun.reflect.GeneratedMethodAccessor377.invoke(Unknown Source) at 
  sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at 
  java.lang.reflect.Method.invoke(Unknown Source) at 
  coldfusion.runtime.StructBean.invoke(StructBean.java:536) at 
  coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2393) at 
  cftest2ecfm989071068.runPage(D:\Web\internet\fboc\test.cfm:19) at 
  coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:231) at 
  coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:416) at 
  coldfusion.runtime.CfJspPage._emptyTcfTag(CfJspPage.java:2722) at 
  cfApplication2ecfc1705903666$funcONREQUEST.runFunction(D:\Web\internet\fboc\Application.cfc:70) at 
  coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:472) at 
  coldfusion.runtime.UDFMethod$ReturnTypeFilter.invoke(UDFMethod.java:405) at 
  coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:368) at 
  coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:55) at 
  coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:321) at 
  coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:220) at 
  coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:491) at 
  coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:337) at 
  coldfusion.runtime.AppEventInvoker.invoke(AppEventInvoker.java:88) at 
  coldfusion.runtime.AppEventInvoker.onRequest(AppEventInvoker.java:280) at 
  coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:356) at 
  coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:48) at 
  coldfusion.filter.MonitoringFilter.invoke(MonitoringFilter.java:40) at 
  coldfusion.filter.PathFilter.invoke(PathFilter.java:94) at 
  coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:70) at 
  coldfusion.filter.BrowserDebugFilter.invoke(BrowserDebugFilter.java:79) at 
  coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28) at 
  coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38) at 
  coldfusion.filter.NoCacheFilter.invoke(NoCacheFilter.java:46) at 
  coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38) at 
  coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22) at 
  coldfusion.filter.CachingFilter.invoke(CachingFilter.java:62) at 
  coldfusion.filter.RequestThrottleFilter.invoke(RequestThrottleFilter.java:126) at 
  coldfusion.CfmServlet.service(CfmServlet.java:201) at 
  coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:89) at 
  jrun.servlet.FilterChain.doFilter(FilterChain.java:86) at 
  coldfusion.monitor.event.MonitoringServletFilter.doFilter(MonitoringServletFilter.java:42) at 
  coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:46) at 
  jrun.servlet.FilterChain.doFilter(FilterChain.java:94) at 
  jrun.servlet.FilterChain.service(FilterChain.java:101) at 
  jrun.servlet.ServletInvoker.invoke(ServletInvoker.java:106) at 
  jrun.servlet.JRunInvokerChain.invokeNext(JRunInvokerChain.java:42) at 
  jrun.servlet.JRunRequestDispatcher.invoke(JRunRequestDispatcher.java:286) at 
  jrun.servlet.ServletEngineService.dispatch(ServletEngineService.java:543) at 
  jrun.servlet.jrpp.JRunProxyService.invokeRunnable(JRunProxyService.java:203) at 
  jrunx.scheduler.ThreadPool$ThreadThrottle.invokeRunnable(ThreadPool.java:428) at 
  jrunx.scheduler.WorkerThread.run(WorkerThread.java:66)
  </code></pre>
  
  <p>Here is a simple test script. You will notice that I have one line commented out. That line works without the <code>ErrorList</code> but throws an error (as it should). I am trying to get the method to work without throwing the error:</p>
  
  <pre><code>&lt;cftry&gt;
  &lt;cfsilent&gt;
  &lt;cfparam name="form.TestField" default="" type="string" /&gt;
  
  &lt;cfset Esapi = CreateObject("java", "org.owasp.esapi.ESAPI") /&gt;
  &lt;cfset EsapiEncoder = Esapi.encoder() /&gt;
  &lt;cfset EsapiValidator = Esapi.validator() /&gt;
  
  &lt;cfset Clean = StructNew() /&gt;
  &lt;cfset Clean.Css = EsapiEncoder.encodeForCss(form.TestField) /&gt;
  &lt;cfset Clean.Html = EsapiEncoder.encodeForHtml(form.TestField) /&gt;
  &lt;cfset Clean.HtmlAttribute = EsapiEncoder.encodeForHtmlAttribute(form.TestField) /&gt;
  &lt;cfset Clean.JavaScript = EsapiEncoder.encodeForJavaScript(form.TestField) /&gt;
  &lt;cfset Clean.Url = EsapiEncoder.encodeForUrl(form.TestField) /&gt;
  &lt;cfset Clean.Xml = EsapiEncoder.encodeForXml(form.TestField) /&gt;
  
  &lt;cfset ErrorList = CreateObject("java", "org.owasp.esapi.ValidationErrorList") /&gt;
  &lt;cfset Valid = StructNew() /&gt;
  &lt;cfset Valid.Input = EsapiValidator.getValidInput("Test Field", form.TestField, "SafeString", 128, false, true, ErrorList) /&gt;
  &lt;!---&lt;cfset Valid.Input = EsapiValidator.getValidInput("Test Field", form.TestField, "SafeString", 128, false, true) /&gt;---&gt;
  &lt;/cfsilent&gt;
  
  &lt;!DOCTYPE HTML&gt;
  &lt;head&gt;
      &lt;meta charset='UTF-8' /&gt;
      &lt;title&gt;ESAPI Test&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
      &lt;div&gt;
          &lt;h3&gt;ESAPI Test&lt;/h3&gt;
          &lt;cfoutput&gt;
          &lt;form name="frmtest" id="frmtest" action="#cgi.script_name#" method="post"&gt;
              &lt;p&gt;Enter text to test:&lt;/p&gt;
              &lt;p&gt;&lt;input type="text" name="TestField" id="TestField" size="64" maxlength="128" value="#Clean.HtmlAttribute#" /&gt;&lt;/p&gt;
              &lt;p&gt;&lt;input type="submit" name="submit" id="submit" value=" Submit " /&gt;&lt;/p&gt;
          &lt;/form&gt;
          &lt;/cfoutput&gt;
          &lt;hr /&gt;
          &lt;cfdump var="#Clean#" label="Clean Structure" /&gt;
          &lt;hr /&gt;
          &lt;cfdump var="#Valid#" label="Valid Structure" /&gt;
      &lt;/div&gt;
  &lt;/body&gt;
  &lt;/html&gt;
  &lt;cfcatch type="any"&gt;
      &lt;hr /&gt;
      &lt;div&gt;
          &lt;h3&gt;ERROR&lt;/h3&gt;
          &lt;cfdump var="#cfcatch#" label="Error" /&gt;
      &lt;/div&gt;
  &lt;/cfcatch&gt;
  &lt;/cftry&gt;
  </code></pre>
  
  <p>When I run this script with "valid" data it works fine (no errors thrown). If I enter an "invalid" character then I get the null pointer exception.</p>
  
  <p>Example of "valid" data: <code>this is a safe string 0123456789</code><br>
  Example of "invalid" data: <code>this is a safe string 0123456789-</code> (notice the hyphen at the end)</p>
  
  <p><a href="http://owasp-esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/reference/DefaultValidator.html#getValidInput%28java.lang.String,%20java.lang.String,%20java.lang.String,%20int,%20boolean,%20org.owasp.esapi.ValidationErrorList%29" rel="nofollow">Here is a link to the documentation for the validator.getValidInput method that I am trying to implement</a>.</p>
  
  <p><a href="http://owasp-esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/ValidationErrorList.html" rel="nofollow">Here is a link to the documentation that shows what I am trying to implement</a>.</p>
  
  <p>For what it's worth, the validation "rules" are defined in the <code>validation.properties</code> file that comes with ColdFusion. That file is located in the {cfusion lib} directory. Here are the contents of that file from my server:</p>
  
  <pre><code># The ESAPI validator does many security checks on input, such as canonicalization
  # and whitelist validation. Note that all of these validation rules are applied *after*
  # canonicalization. Double-encoded characters (even with different encodings involved,
  # are never allowed.
  #
  # To use:
  #
  # First set up a pattern below. You can choose any name you want, prefixed by the word
  # "Validation." For example:
  #   Validation.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
  # 
  # Then you can validate in your code against the pattern like this:
  #     ESAPI.validator().isValidInput("User Email", input, "Email", maxLength, allowNull);
  # Where maxLength and allowNull are set for you needs, respectively.
  #
  # But note, when you use boolean variants of validation functions, you lose critical 
  # canonicalization. It is preferable to use the "get" methods (which throw exceptions) and 
  # and use the returned user input which is in canonical form. Consider the following:
  #  
  # try {
  #    someObject.setEmail(ESAPI.validator().getValidInput("User Email", input, "Email", maxLength, allowNull));
  #
  Validator.SafeString=^[.\\p{Alnum}\\p{Space}]{0,1024}$
  Validator.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
  Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
  Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&amp;amp;%\\$#_]*)?$
  Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
  Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$
  Validator.CFContainerID=^[\\p{Alnum}_\\-\\.:]+$
  Validator.GOOGLEMAPAPI=^[\\p{Alnum}_\\+=\\/\\-]+$
  Validator.CFFORMSCRIPTSRC=^[^\\*\\?\"'&lt;&gt;|%]*$
  </code></pre>
  
  <p>I presume that the idea is to add rules to this file for your own applications.</p>
  
  <p>Has anyone gotten the <code>validator.getValidInput()</code> method to work in <em>batch</em> (groups of validation attempts)?</p>
  
  <p><br>
  <strong>Update 1</strong></p>
  
  <p>I noticed that the following is being written to my <code>cfusion-out.log</code> on the server every time I get the null pointer exception. It leads me to believe that it is working to a point but then gets a null pointer while attempting to assign the validation exception:</p>
  
  <pre><code>06/25 16:08:14 [jrpp-3225] WARN  [SECURITY FAILURE Anonymous:null@unknown -&gt; /IntrusionDetector] Invalid input: context=Test Field, type(SafeString)=^[.\p{Alnum}\p{Space}]{0,1024}$, input=this is a safe string 0123456789-
  org.owasp.esapi.errors.ValidationException: Test Field: Invalid input. Please conform to regex ^[.\p{Alnum}\p{Space}]{0,1024}$ with a maximum length of 128
  at org.owasp.esapi.reference.validation.StringValidationRule.checkWhitelist(StringValidationRule.java:144)
  at org.owasp.esapi.reference.validation.StringValidationRule.checkWhitelist(StringValidationRule.java:160)
  at org.owasp.esapi.reference.validation.StringValidationRule.getValid(StringValidationRule.java:284)
  at org.owasp.esapi.reference.DefaultValidator.getValidInput(DefaultValidator.java:199)
  at org.owasp.esapi.reference.DefaultValidator.getValidInput(DefaultValidator.java:236)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
  at java.lang.reflect.Method.invoke(Unknown Source)
  at coldfusion.runtime.StructBean.invoke(StructBean.java:508)
  at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2393)
  at cftest2ecfm989071068.runPage(D:\Web\internet\fboc\test.cfm:19)
  at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:231)
  at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:416)
  at coldfusion.runtime.CfJspPage._emptyTcfTag(CfJspPage.java:2722)
  at cfApplication2ecfc1705903666$funcONREQUEST.runFunction(D:\Web\internet\fboc\Application.cfc:70)
  at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:472)
  at coldfusion.runtime.UDFMethod$ReturnTypeFilter.invoke(UDFMethod.java:405)
  at coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:368)
  at coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:55)
  at coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:321)
  at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:220)
  at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:491)
  at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:337)
  at coldfusion.runtime.AppEventInvoker.invoke(AppEventInvoker.java:88)
  at coldfusion.runtime.AppEventInvoker.onRequest(AppEventInvoker.java:280)
  at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:356)
  at coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:48)
  at coldfusion.filter.MonitoringFilter.invoke(MonitoringFilter.java:40)
  at coldfusion.filter.PathFilter.invoke(PathFilter.java:94)
  at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:70)
  at coldfusion.filter.BrowserDebugFilter.invoke(BrowserDebugFilter.java:79)
  at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28)
  at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38)
  at coldfusion.filter.NoCacheFilter.invoke(NoCacheFilter.java:46)
  at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38)
  at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22)
  at coldfusion.filter.CachingFilter.invoke(CachingFilter.java:62)
  at coldfusion.filter.RequestThrottleFilter.invoke(RequestThrottleFilter.java:126)
  at coldfusion.CfmServlet.service(CfmServlet.java:201)
  at coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:89)
  at jrun.servlet.FilterChain.doFilter(FilterChain.java:86)
  at coldfusion.monitor.event.MonitoringServletFilter.doFilter(MonitoringServletFilter.java:42)
  at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:46)
  at jrun.servlet.FilterChain.doFilter(FilterChain.java:94)
  at jrun.servlet.FilterChain.service(FilterChain.java:101)
  at jrun.servlet.ServletInvoker.invoke(ServletInvoker.java:106)
  at jrun.servlet.JRunInvokerChain.invokeNext(JRunInvokerChain.java:42)
  at jrun.servlet.JRunRequestDispatcher.invoke(JRunRequestDispatcher.java:286)
  at jrun.servlet.ServletEngineService.dispatch(ServletEngineService.java:543)
  at jrun.servlet.jrpp.JRunProxyService.invokeRunnable(JRunProxyService.java:203)
  at jrunx.scheduler.ThreadPool$ThreadThrottle.invokeRunnable(ThreadPool.java:428)
  at jrunx.scheduler.WorkerThread.run(WorkerThread.java:66)
  </code></pre>
  
  <p><br>
  <strong>Update 2</strong></p>
  
  <p>I have been digging through <a href="https://github.com/damonmiller/esapi4cf/blob/master/esapi4cf/org/owasp/esapi/reference/DefaultValidator.cfc" rel="nofollow">Damon Miller's implementation of the OWASP ESAPI</a> methods for ColdFusion. I noticed in his code that he does not call the <code>getValidInput()</code> method with the <code>ValidationErrorList</code> attribute. Rather he wrote the code to catch the generated error and then add the error to the list himself.  Hmmm?  I thought the method was supposed to do that for you????</p>
  
  <p>As an aside, I am trying not to use a library such as his to avoid the additional bloat that I do not need.</p>
  
  <p>Excerpt from his code:</p>
  
  <pre><code>if(structKeyExists( arguments, "errorList" )) {
          try {
              return getValidInput( arguments.context, arguments.input, arguments.type, arguments.maxLength, arguments.allowNull );
          }
          catch(esapi4cf.org.owasp.esapi.errors.ValidationException e) {
              arguments.errorList.addError( arguments.context, e );
          }
          return arguments.input;
      }
      else {
      ...
  </code></pre>