Search code examples
jspesapi

esapi encodeForJavaScript Newlines Limitation?


We introduced esapi into our Spring JSP project to handle sanitizing data being rendered into HTML and Javascript. One field we use is set by a third-party webservice and we output the result into a Javascript variable:

var errReason= '<esapi:encodeForJavaScript>${myOrder.rateErrorMessage}</esapi:encodeForJavaScript>';

The problem is that the result from the WebService sometimes has a newline character appended to the end. I would have thought that encodeForJavascript should handle newlines, but instead the above code renders like this:

var errReason 'This was an invalid request: missing required parameter
';

Is esapi functioning as designed? Are there any alternatives for dealing with this issue?

Thanks.


Solution

  • With the given information, it cannot be esapi that is causing your problem here. The following unit test passes:

    @Test
    public void testWindowsNewline() { 
         Encoder instance = ESAPI.encoder(); 
         assertEquals("\\x0D\\x0A", instance.encodeForJavaScript("\r\n")); 
    }
    

    FWIW, the taglibs are just wrappers around the esapi encoder class.

    It appears that you're using the taglibs embedded within javascript strings, which is something that if I saw on a code review on my team I would mark as a defect, and here's why:

    There are multiple steps a JSP compiler goes through when it is rendering a page. You have HTML rendering, data binding, and JSTL binding just to name a few. Since this ordering is implementation specific, you want to try and stay away from mixing code like you have here.

    I suspect that the JSP compiler is interpreting the "\r\n" as HTML whitespace before it makes the call to <esapi:encodeForJavaScript></esapi:encodeForJavaScript>.

    Here's a couple of things I've done in the past. I tend to prefer escaping at the Controller/Service layer. This example assumes Spring MVC, and is taking huge liberties.

    @Controller
    public class FooController {
         //This should actually be done in a service class, but this is for demo
         private Encoder = ESAPI.encoder();
    
         @Autowired
         private DataService dataService;
    
         //logic for handling request
    
         public String returnStringEscapedForJavascriptContext(){
              OrderBean myOrder = dataService.getMyOrder(); 
              String unescaped = myOrder.getRateErrorMessage();
              String escapedAsJavascriptData = encoder.encodeForJavaScript(unescaped);
              return escapedAsJavascriptData ;
         }
    }
    

    The same basic logic would apply if you're converting myOrder into a viewBean, etc.

    Another solution would be to wrap safe esapi escaping methods as an abstract class that all your view beans inherited from so that way, if you really HAD to do this work in the JSP, you could have something like this:

    public abstract class SecureBean {
         private Encoder encoder = ESAPI.encoder();
         public String escapeForJavaScript(String input){
              return encoder.escapeForJavaScript(input);
         }
    }
    

    Then inherit:

    public class OrderBean extends SecureBean {
         String rateErrorMessage;  //with getters/setters assumed
    }
    

    Now in your jsp:

    var errReason= '${myOrder.escapeForJavaScript(myOrder.rateErrorMessage())}';
    

    Some of this is a little verbose, but the goal here is to not try and step on the JSP's rendering order. You might also consider writing an adapter interface for encoding/decoding so you can swap out Encoder implementations--esapi has lost OWASP flagship status and hasn't had a major revision in ~1.5yrs.