Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fix for GRAILS-9247 and GRAILS-9392 g:fieldValue encoding issues

Conflicts:

	grails-web/src/main/groovy/org/codehaus/groovy/grails/web/taglib/GroovyPageAttributes.java
  • Loading branch information...
commit c9b14df09dea9d94479bf7e6fc7f0252824c15e9 1 parent 10aeae6
@lhotari lhotari authored
View
3  grails-plugin-codecs/src/main/groovy/org/codehaus/groovy/grails/plugins/codecs/HTMLCodec.java
@@ -14,6 +14,7 @@
*/
package org.codehaus.groovy.grails.plugins.codecs;
+import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes;
import org.codehaus.groovy.grails.web.util.StreamCharBuffer;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
@@ -40,7 +41,7 @@ public static CharSequence encode(Object target) {
public static boolean shouldEncode() {
final RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
if (attributes != null) {
- Object codecName = attributes.getAttribute("org.codehaus.groovy.grails.GSP_CODEC",
+ Object codecName = attributes.getAttribute(GrailsApplicationAttributes.GSP_CODEC,
RequestAttributes.SCOPE_REQUEST);
if (codecName != null && codecName.toString().equalsIgnoreCase("html")) {
return false;
View
14 grails-plugin-gsp/src/main/groovy/org/codehaus/groovy/grails/plugins/web/taglib/ValidationTagLib.groovy
@@ -23,6 +23,7 @@ import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import org.apache.commons.lang.StringEscapeUtils
import org.codehaus.groovy.grails.plugins.codecs.HTMLCodec
+import org.codehaus.groovy.grails.web.taglib.GroovyPageAttributes;
import org.springframework.beans.PropertyEditorRegistry
import org.springframework.context.MessageSourceResolvable
import org.springframework.context.NoSuchMessageException
@@ -76,6 +77,9 @@ class ValidationTagLib {
Closure fieldValue = { attrs, body ->
def bean = attrs.bean
def field = attrs.field?.toString()
+
+ def tagSyntaxCall = (attrs instanceof GroovyPageAttributes) ? attrs.isGspTagSyntaxCall() : false
+
if (bean && field) {
if (bean.metaClass.hasProperty(bean,'errors')) {
Errors errors = bean.errors
@@ -87,7 +91,7 @@ class ValidationTagLib {
}
}
if (rejectedValue != null) {
- out << formatValue(rejectedValue, field)
+ out << formatValue(rejectedValue, field, tagSyntaxCall)
}
}
else {
@@ -96,7 +100,7 @@ class ValidationTagLib {
rejectedValue = rejectedValue?."$fieldPart"
}
if (rejectedValue != null) {
- out << formatValue(rejectedValue, field)
+ out << formatValue(rejectedValue, field, tagSyntaxCall)
}
}
}
@@ -422,12 +426,12 @@ class ValidationTagLib {
* formatted according to the current user's locale during the
* conversion to a string.
*/
- def formatValue(value, String propertyPath = null) {
+ def formatValue(value, String propertyPath = null, Boolean tagSyntaxCall = false) {
PropertyEditorRegistry registry = RequestContextHolder.currentRequestAttributes().getPropertyEditorRegistry()
PropertyEditor editor = registry.findCustomEditor(value.getClass(), propertyPath)
if (editor) {
editor.setValue(value)
- return HTMLCodec.shouldEncode() && !(value instanceof Number) ? editor.asText?.encodeAsHTML() : editor.asText
+ return (tagSyntaxCall || HTMLCodec.shouldEncode()) && !(value instanceof Number) ? editor.asText?.encodeAsHTML() : editor.asText
}
if (value instanceof Number) {
@@ -445,6 +449,6 @@ class ValidationTagLib {
value = message(message: value)
}
- return HTMLCodec.shouldEncode() ? value.toString().encodeAsHTML() : value
+ return (tagSyntaxCall || HTMLCodec.shouldEncode()) ? value.toString().encodeAsHTML() : value
}
}
View
44 grails-test-suite-web/src/test/groovy/org/codehaus/groovy/grails/web/taglib/ValidationTagLibTests.groovy
@@ -1,5 +1,6 @@
package org.codehaus.groovy.grails.web.taglib
+import org.codehaus.groovy.grails.support.MockStringResourceLoader
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.springframework.beans.factory.support.RootBeanDefinition
import org.springframework.context.MessageSourceResolvable
@@ -118,14 +119,49 @@ enum Title implements org.springframework.context.MessageSourceResolvable {
b.properties = [title:"<script>alert('escape me')</script>"]
def template = '''<g:fieldValue bean="${book}" field="title" />'''
+ def htmlCodecDirective = '<%@page defaultCodec="HTML" %>'
+ def expected = "&lt;script&gt;alert(&#39;escape me&#39;)&lt;/script&gt;"
+ assertOutputEquals(expected, template, [book:b])
+ assertOutputEquals(expected, htmlCodecDirective + template, [book:b])
+ }
+
+ void testFieldValueHtmlEscapingWithFunctionSyntaxCall() {
+ def b = ga.getDomainClass("ValidationTagLibBook").newInstance()
+ b.properties = [title:"<script>alert('escape me')</script>"]
- assertOutputEquals("&lt;script&gt;alert(&#39;escape me&#39;)&lt;/script&gt;", template, [book:b])
+ def template = '''${fieldValue(bean:book, field:"title")}'''
+ def htmlCodecDirective = '<%@page defaultCodec="HTML" %>'
+ def expected = "&lt;script&gt;alert(&#39;escape me&#39;)&lt;/script&gt;"
+ assertOutputEquals(expected, template, [book:b])
+ assertOutputEquals(expected, htmlCodecDirective + template, [book:b])
+ }
- request.setAttribute("org.codehaus.groovy.grails.GSP_CODEC", 'html')
+ void testFieldValueHtmlEscapingDifferentEncodings() {
+ def b = ga.getDomainClass("ValidationTagLibBook").newInstance()
+ b.properties = [title:"<script>alert('escape me')</script>"]
- assertOutputEquals("<script>alert('escape me')</script>", template, [book:b])
+ def template = '''${fieldValue(bean:book, field:"title")}'''
+ def htmlCodecDirective = '<%@page defaultCodec="HTML" %>'
+ def expected = "&lt;script&gt;alert(&#39;escape me&#39;)&lt;/script&gt;"
+
+ def resourceLoader = new MockStringResourceLoader()
+ resourceLoader.registerMockResource('/_sometemplate.gsp', htmlCodecDirective + template)
+ resourceLoader.registerMockResource('/_sometemplate_nocodec.gsp', template)
+ appCtx.groovyPagesTemplateEngine.groovyPageLocator.addResourceLoader(resourceLoader)
+
+ assertOutputEquals(expected, '<g:render template="/sometemplate" model="[book:book]" />', [book:b])
+ assertOutputEquals(expected + expected, template + '<g:render template="/sometemplate" model="[book:book]" />', [book:b])
+ assertOutputEquals(expected + expected, htmlCodecDirective + template + '<g:render template="/sometemplate" model="[book:book]" />', [book:b])
+ assertOutputEquals(expected + expected, '<g:render template="/sometemplate" model="[book:book]" />' + template, [book:b])
+ assertOutputEquals(expected + expected, htmlCodecDirective + '<g:render template="/sometemplate" model="[book:book]" />' + template, [book:b])
+
+ assertOutputEquals(expected, '<g:render template="/sometemplate_nocodec" model="[book:book]" />', [book:b])
+ assertOutputEquals(expected + expected, template + '<g:render template="/sometemplate_nocodec" model="[book:book]" />', [book:b])
+ assertOutputEquals(expected + expected, htmlCodecDirective + template + '<g:render template="/sometemplate_nocodec" model="[book:book]" />', [book:b])
+ assertOutputEquals(expected + expected, '<g:render template="/sometemplate_nocodec" model="[book:book]" />' + template, [book:b])
+ assertOutputEquals(expected + expected, htmlCodecDirective + '<g:render template="/sometemplate_nocodec" model="[book:book]" />' + template, [book:b])
}
-
+
void testFieldValueTag() {
def b = ga.getDomainClass("ValidationTagLibBook").newInstance()
b.properties = [publisherURL:"a_bad_url"]
View
4 grails-web/src/main/groovy/org/codehaus/groovy/grails/web/pages/GroovyPage.java
@@ -385,6 +385,7 @@ public final void invokeTag(String tagName, String tagNamespace, int lineNumber,
if (!(attrs instanceof GroovyPageAttributes)) {
attrs = new GroovyPageAttributes(attrs);
}
+ ((GroovyPageAttributes)attrs).setGspTagSyntaxCall(true);
switch (tag.getParameterTypes().length) {
case 1:
tagresult = tag.call(new Object[]{attrs});
@@ -485,8 +486,9 @@ public final static Object captureTagOutput(TagLibraryLookup gspTagLibraryLookup
String tagName, Map attrs, Object body, GrailsWebRequest webRequest) {
if (!(attrs instanceof GroovyPageAttributes)) {
- attrs = new GroovyPageAttributes(attrs);
+ attrs = new GroovyPageAttributes(attrs, false);
}
+ ((GroovyPageAttributes)attrs).setGspTagSyntaxCall(false);
GroovyObject tagLib = lookupCachedTagLib(gspTagLibraryLookup, namespace, tagName);
if (tagLib == null) {
View
9 grails-web/src/main/groovy/org/codehaus/groovy/grails/web/pages/GroovyPageWritable.java
@@ -157,15 +157,21 @@ public Writer writeTo(Writer out) throws IOException {
}
GroovyPageBinding binding = createBinding(parentBinding);
+ String previousGspCode = null;
if (hasRequest) {
request.setAttribute(GrailsApplicationAttributes.PAGE_SCOPE, binding);
+ previousGspCode = (String)request.getAttribute(GrailsApplicationAttributes.GSP_CODEC);
}
+
if (metaInfo.getCodecClass() != null) {
if (hasRequest) {
- request.setAttribute("org.codehaus.groovy.grails.GSP_CODEC", metaInfo.getCodecName());
+ request.setAttribute(GrailsApplicationAttributes.GSP_CODEC, metaInfo.getCodecName());
}
binding.setVariableDirectly(GroovyPage.CODEC_VARNAME, metaInfo.getCodecClass());
} else {
+ if (hasRequest) {
+ request.setAttribute(GrailsApplicationAttributes.GSP_CODEC, null);
+ }
binding.setVariableDirectly(GroovyPage.CODEC_VARNAME, gspNoneCodeInstance);
}
binding.setVariableDirectly(GroovyPage.RESPONSE, response);
@@ -213,6 +219,7 @@ public Writer writeTo(Writer out) throws IOException {
} else {
request.setAttribute(GrailsApplicationAttributes.PAGE_SCOPE, parentBinding);
}
+ request.setAttribute(GrailsApplicationAttributes.GSP_CODEC, previousGspCode);
}
}
if (debugTemplates) {
View
1  grails-web/src/main/groovy/org/codehaus/groovy/grails/web/servlet/GrailsApplicationAttributes.java
@@ -51,6 +51,7 @@
String TAG_CACHE = "org.codehaus.groovy.grails.TAG_CACHE";
String ID_PARAM = "id";
String GSP_TO_RENDER = "org.codehaus.groovy.grails.GSP_TO_RENDER";
+ String GSP_CODEC = "org.codehaus.groovy.grails.GSP_CODEC";
String WEB_REQUEST = "org.codehaus.groovy.grails.WEB_REQUEST";
String PAGE_SCOPE = "org.codehaus.groovy.grails.PAGE_SCOPE";
String GSP_TMP_WRITER = "org.codehaus.groovy.grails.GSP_TMP_WRITER";
View
16 grails-web/src/main/groovy/org/codehaus/groovy/grails/web/taglib/GroovyPageAttributes.java
@@ -29,13 +29,27 @@
* @since 1.2
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
-public class GroovyPageAttributes extends TypeConvertingMap implements Cloneable {
+public class GroovyPageAttributes extends TypeConvertingMap implements Cloneable {
+ boolean gspTagSyntaxCall=true;
public GroovyPageAttributes() {
super();
}
public GroovyPageAttributes(Map map) {
+ this(map, true);
+ }
+
+ public GroovyPageAttributes(Map map, boolean gspTagSyntaxCall) {
super(map);
+ this.gspTagSyntaxCall=gspTagSyntaxCall;
+ }
+
+ public boolean isGspTagSyntaxCall() {
+ return gspTagSyntaxCall;
+ }
+
+ public void setGspTagSyntaxCall(boolean gspTagSyntaxCall) {
+ this.gspTagSyntaxCall=gspTagSyntaxCall;
}
@Override
Please sign in to comment.
Something went wrong with that request. Please try again.