Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Escape error defaultMessage strings due to possibility of user input being present #322

Merged
merged 1 commit into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>'
}
}