Skip to content

Commit

Permalink
Merge pull request #322 from calnet-oss/err_escaping
Browse files Browse the repository at this point in the history
Escape error defaultMessage strings due to possibility of user input being present
  • Loading branch information
sbglasius committed Feb 9, 2023
2 parents 16950df + ba00ce8 commit 12f4b51
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ import org.grails.scaffolding.model.property.DomainPropertyFactory
import org.grails.web.servlet.mvc.GrailsWebRequest
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.MessageSource
import org.springframework.context.MessageSourceResolvable
import org.springframework.context.NoSuchMessageException
import org.springframework.web.servlet.LocaleResolver

import javax.servlet.http.HttpServletRequest
Expand Down Expand Up @@ -68,6 +71,7 @@ class FormFieldsTagLib {
DomainModelService domainModelService
LocaleResolver localeResolver
CodecLookup codecLookup
MessageSource messageSource

static defaultEncodeAs = [taglib: 'raw']

Expand Down Expand Up @@ -458,7 +462,18 @@ class FormFieldsTagLib {
value : (value instanceof Number || value instanceof Boolean || value) ? value : valueDefault,
constraints : propertyAccessor.constraints,
persistentProperty: propertyAccessor.domainProperty,
errors : propertyAccessor.errors.collect { message(error: it) },
errors : propertyAccessor.errors.collect { error ->
String errorMsg = null
try {
errorMsg = error instanceof MessageSourceResolvable ? messageSource.getMessage(error, locale) : messageSource.getMessage(error.toString(), null, locale)
}
catch (NoSuchMessageException ignored) {
// no-op
}
// unresolved message codes fallback to the defaultMessage and this should
// be escaped as it could be an error message with user input
errorMsg && errorMsg == error.defaultMessage ? message(error: error, encodeAs: "HTML") : message(error: error)
},
required : attrs.containsKey("required") ? Boolean.valueOf(attrs.remove('required')) : propertyAccessor.required,
invalid : attrs.containsKey("invalid") ? Boolean.valueOf(attrs.remove('invalid')) : propertyAccessor.invalid,
prefix : resolvePrefix(attrs.remove('prefix')),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package grails.plugin.formfields.taglib

import grails.plugin.formfields.mock.Gender
import grails.plugin.formfields.mock.Person
import grails.testing.web.taglib.TagLibUnitTest
import grails.validation.ValidationErrors
import org.grails.plugins.web.taglib.FormatTagLib
import org.springframework.context.support.StaticMessageSource
import spock.lang.Issue
import grails.plugin.formfields.*

Expand Down Expand Up @@ -56,4 +60,44 @@ class FieldTagWithBodySpec extends AbstractFormFieldsTagLibSpec implements TagLi
expect:
applyTemplate('<f:field bean="personInstance" property="name" input-foo="bar">${attrs.foo}</f:field>', [personInstance: personInstance]) == 'bar'
}

@Issue("https://github.com/grails-fields-plugin/grails-fields/issues/323")
void 'validation defaultMessage strings are escaped'() {
given:
views['/_fields/default/_wrapper.gsp'] = '${widget}'

and:
def person = new Person(name: 'Not Allowed', gender: Gender.Male, password: 'XYZ').with {
errors = new ValidationErrors(it)
errors.rejectValue('name', 'unresolved.code', 'custom error with special chars & < > \' "')
it
}

when:
def result = applyTemplate('<f:field bean="personInstance" property="name" encodeAs="raw">${errors[0]}</f:field>', [personInstance: person])

then:
result == 'custom error with special chars &amp; &lt; &gt; &#39; &quot;'
}

void 'resolved error codes are not escaped'() {
given:
views['/_fields/default/_wrapper.gsp'] = '${widget}'

and:
((StaticMessageSource) messageSource).addMessage('name.invalid', FormatTagLib.resolveLocale(null), '<div>Name is invalid</div>')

and:
def person = new Person(name: 'Not Allowed', gender: Gender.Male, password: 'XYZ').with {
errors = new ValidationErrors(it)
errors.rejectValue('name', 'name.invalid', 'default error message')
it
}

when:
def result = applyTemplate('<f:field bean="personInstance" property="name" encodeAs="raw">${errors[0]}</f:field>', [personInstance: person])

then:
result == '<div>Name is invalid</div>'
}
}

0 comments on commit 12f4b51

Please sign in to comment.