Search code examples
javaspringspring-mvcfreemarker

Spring MVC - spring.ftl is causing double escaping of special characters


So I am using Spring 5.3.xx right now with a simple ftl file (i have simplified it for now)

<#import "/spring.ftl" as spring/>
<@spring.formInput path="${textInputName}" attributes='maxlength="1024"  '/>

with some simple free-marker settings from context.xml

       <property name="freemarkerSettings">
            <props>
                <prop key="datetime_format">yyyy-MM-dd</prop>
                <prop key="url_escaping_charset">UTF-8</prop>
            </props>
        </property>

And i have enable escaping in web.xml

    <context-param>
        <param-name>defaultHtmlEscape</param-name>
        <param-value>true</param-value>
    </context-param>

I am facing an issue with double escaping. A simple value like "abc" in input will be render as &quot;abc&quot; in input value on a page reload.

  • One is from spring when it tries to bind status

https://github.com/spring-projects/spring-framework/blob/08bc1a050ec87cdaad6b05170c27e34d3f90cafa/spring-webmvc/src/main/java/org/springframework/web/servlet/support/BindStatus.java#L166-L168

  • Other one is from ftl since the spring.ftl uses #ftl output_format="HTML" and that causes autoEscape below to be set to true

https://github.com/apache/freemarker/blob/df938ce6120ca155b87d48df97b3a6d62123b17f/src/main/java/freemarker/core/DollarVariable.java#L67-L71

Are we suppose to turn off escaping from spring from web.xml (which don't seem safe to me) or is it something else that I missed ?

ref: https://github.com/spring-projects/spring-framework/issues/19306


Solution

  • TLDR; just leave the defaultHtmlEscape context parameter unset, and don't use some ancient Spring/JSTL version, then both JSP and FreeMarker will escape HTML form values exactly once. Especially if you don't use JSP, you have no much to fear, as spring.formInput is hard-wired to do HTML escaping in spring.ftl.

    Longer version...

    My experience is that, at least with Spring 5.2.3 (comes with JSTL 1.2.7), the JSP form:input tag escapes by default, if you don't set the defaultHtmlEscape context parameter at all. Also, the FreeMarker spring.formInput only escapes via ${...} FreeMarker auto-escaping then, not with the JSTL binding escaping mechanism. So by default both JSP and FreeMarker escapes exactly once.

    OTOH if you set defaultHtmlEscape to anything (true or false), one of the two (JSP or FreeMarker form support) will be broken.

    But, if there's some company policy that requires you to have defaultHtmlEscape=true, then you can do this workaround:

    <#import "/spring.ftl" as spring/>
    <#assign htmlEscape = false in spring>
    ...
    <@spring.formInput "user.name" />
    

    There, <#assign htmlEscape = false in spring> disables the JSTL binding escaping for this response generation only, but spring.formInput will still do its own escaping.

    Update: There's a caveat with <#assign htmlEscape = false in spring>. <@spring.message ... /> is not affected by it, but it's affected by defaultHtmlEscape. If defaultHtmlEscape is not set, or it's set to false the message is not escaped. If defaultHtmlEscape is set to true, the message is escaped, and you can't avoid that (as far as you are using that macro). But there's no double escaping.