Skip to content

Commit

Permalink
Update SimpleDataBinder to prevent binding to specific types (#13259)
Browse files Browse the repository at this point in the history
* Escaped Javadoc special chars

Escaping of special chars in Javadoc, such as '<' with '&lt;'
Refactoring of code -> Define explicit types and remove unnecessary variable usages.

* Fixed Javadoc typos

* Update GrailsPrintWriter.java
  • Loading branch information
puneetbehl authored Dec 5, 2023
1 parent 1373750 commit c401faa
Show file tree
Hide file tree
Showing 11 changed files with 43 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Contact{
}
class User {
&#064;BindInitializer({
obj -> new Contact(account:obj.account)
obj -&gt; new Contact(account:obj.account)
})
Contact contact
Account account
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@
* When the annotation is applied to a field, the value assigned to the
* annotation should be a Closure which accepts 2 parameters. The first
* parameter is the object that data binding is being applied to. The second
* parameter is a {@link org.grails.databinding.DataBindingSource} containing the values being bound to the object.
* parameter is a {@link grails.databinding.DataBindingSource} containing the values being bound to the object.
* The value returned by the Closure will be bound to the field. The
* following code demonstrates using this technique to bind an upper
* case version of the value in the DataBindingSource to the field.
*
<pre>
class SomeClass {
&#064;BindUsing({
obj, source -> source['name']?.toUpperCase()
obj, source -&gt; source['name']?.toUpperCase()
})
String name
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import grails.databinding.initializers.ValueInitializer
import groovy.transform.CompileStatic
import groovy.transform.TypeCheckingMode
import groovy.util.slurpersupport.GPathResult
import org.codehaus.groovy.reflection.CachedMethod
import org.grails.databinding.ClosureValueConverter
import org.grails.databinding.ClosureValueInitializer
import org.grails.databinding.IndexedPropertyReferenceDescriptor
Expand Down Expand Up @@ -81,7 +82,7 @@ class SimpleDataBinder implements DataBinder {
Float,
Double,
Character
]
] as List<Class>

static final INDEXED_PROPERTY_REGEX = /(.*)\[\s*([^\s]*)\s*\]\s*$/

Expand Down Expand Up @@ -260,14 +261,14 @@ class SimpleDataBinder implements DataBinder {
}

protected boolean isOkToBind(String propName, List whiteList, List blackList) {
'class' != propName && 'classLoader' != propName && 'protectionDomain' != propName && 'metaClass' != propName && !blackList?.contains(propName) && (!whiteList || whiteList.contains(propName) || whiteList.find { it -> it?.toString()?.startsWith(propName + '.')})
'class' != propName && 'classLoader' != propName && 'protectionDomain' != propName && 'metaClass' != propName && 'metaPropertyValues' != propName && 'properties' != propName && !blackList?.contains(propName) && (!whiteList || whiteList.contains(propName) || whiteList.find { it -> it?.toString()?.startsWith(propName + '.')})
}

protected boolean isOkToBind(MetaProperty property, List whitelist, List blacklist) {
isOkToBind(property.name, whitelist, blacklist) &&
(property.type != null) &&
!Modifier.isStatic(property.modifiers) &&
!(ClassLoader.class.isAssignableFrom(property.type) || ProtectionDomain.class.isAssignableFrom(property.type))
!(ClassLoader.class.isAssignableFrom(property.type) || ProtectionDomain.class.isAssignableFrom(property.type) || MetaProperty.class.isAssignableFrom(property.type) || CachedMethod.class.isAssignableFrom(property.type))
}

protected IndexedPropertyReferenceDescriptor getIndexedPropertyReferenceDescriptor(propName) {
Expand Down Expand Up @@ -498,7 +499,7 @@ class SimpleDataBinder implements DataBinder {
* @see BindingFormat
*/
protected ValueConverter getFormattedConverter(Field field, String formattingValue) {
def converter
ValueConverter converter
def formattedConverter = formattedValueConversionHelpers[field.type]
if (formattedConverter) {
converter = { SimpleMapDataBindingSource source ->
Expand All @@ -517,7 +518,7 @@ class SimpleDataBinder implements DataBinder {
Field field = null
try {
field = clazz.getDeclaredField(fieldName)
} catch (NoSuchFieldException nsfe) {
} catch (NoSuchFieldException ignored) {
def superClass = clazz.getSuperclass()
if(superClass != Object) {
field = getField(superClass, fieldName)
Expand All @@ -527,7 +528,7 @@ class SimpleDataBinder implements DataBinder {
}

protected ValueConverter getValueConverterForField(obj, String propName) {
def converter
ValueConverter converter
try {
def field = getField(obj.getClass(), propName)
if (field) {
Expand All @@ -545,9 +546,9 @@ class SimpleDataBinder implements DataBinder {
}
}
}
} catch (Exception e) {
return converter
} catch (Exception ignored) {
}
converter
}

/**
Expand All @@ -556,11 +557,9 @@ class SimpleDataBinder implements DataBinder {
*/
protected Class getValueOfBindUsing(Annotation annotation) {
assert annotation instanceof BindUsing
def value
if(annotation instanceof BindUsing) {
value = ((BindUsing)annotation).value()
if (annotation instanceof BindUsing) {
return ((BindUsing) annotation).value()
}
value
}

/**
Expand All @@ -569,21 +568,19 @@ class SimpleDataBinder implements DataBinder {
*/
protected String getFormatString(Annotation annotation) {
assert annotation instanceof BindingFormat
String formatString
if(annotation instanceof BindingFormat) {
formatString = ((BindingFormat)annotation).value()
if (annotation instanceof BindingFormat) {
return ((BindingFormat) annotation).value()
}
formatString
}

protected ValueConverter getValueConverterForClass(obj, String propName) {
def converter
ValueConverter converter
def objClass = obj.getClass()
def annotation = objClass.getAnnotation(BindUsing)
if (annotation) {
def valueClass = getValueOfBindUsing(annotation)
if (BindingHelper.isAssignableFrom(valueClass)) {
BindingHelper dataConverter = (BindingHelper)valueClass.newInstance()
BindingHelper dataConverter = (BindingHelper) valueClass.getDeclaredConstructor().newInstance()
converter = new ClosureValueConverter(converterClosure: { DataBindingSource it -> dataConverter.getPropertyValue(obj, propName, it) })
}
}
Expand Down Expand Up @@ -765,7 +762,7 @@ class SimpleDataBinder implements DataBinder {
}

protected ValueInitializer getValueInitializerForField(obj, String propName) {
def initializer
ValueInitializer initializer
try {
def field = getField(obj.getClass(), propName)
if (field) {
Expand Down Expand Up @@ -822,7 +819,7 @@ class SimpleDataBinder implements DataBinder {
bind obj, new SimpleMapDataBindingSource(value)
return obj
} else if (Enum.isAssignableFrom(typeToConvertTo) && value instanceof String) {
return convertStringToEnum(typeToConvertTo, value)
return convertStringToEnum((Class<? extends Enum>) typeToConvertTo, value)
}
typeToConvertTo.newInstance value
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
* into an object. Typically a structured editor will pull
* several values out of the Map that are necessary to initialize
* the state of the object.
<pre>
<code>
class Address {
String state
String city
}
class StructuredAddressBindingEditor implements StructuredBindingEditor {
public Object getPropertyValue(Object obj, String propertyName, Map<String, Object> source) {
public Object getPropertyValue(Object obj, String propertyName, Map&lt;String, Object&gt; source) {
def address = new Address()
address.state = source[propertyName + '_someState']
Expand Down Expand Up @@ -58,7 +58,7 @@ binder.registerStructuredEditor Address, new StructuredAddressBindingEditor()
assert resident.workAddress
assert resident.workAddress.state == "Scott's Work State"
assert resident.workAddress.city == null
</pre>
</code>
*
* @author Jeff Brown
* @since 3.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ Class getTargetType() {
* @author Jeff Brown
* @since 3.0
* @see grails.databinding.BindingFormat
* @see org.grails.databinding.SimpleDataBinder
* @see org.grails.databinding.SimpleDataBinder#registerFormattedValueConverter(FormattedValueConverter)
* @see grails.databinding.SimpleDataBinder
* @see grails.databinding.SimpleDataBinder#registerFormattedValueConverter(FormattedValueConverter)
*/
public interface FormattedValueConverter {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ protected Writer unwrapWriter(Writer writer) {
}

/**
* Provides Groovy << left shift operator, but intercepts call to make sure
* Provides Groovy &lt;&lt; left shift operator, but intercepts call to make sure
* nulls are converted to "" strings
*
* @param obj The value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@
*
* <p>
* StreamCharBuffer keeps the buffer in a linked list of "chunks". The main
* difference compared to JDK in-memory buffers (StringBuffer, StringBuilder &
* difference compared to JDK in-memory buffers (StringBuffer, StringBuilder and
* StringWriter) is that the buffer can be held in several smaller buffers
* ("chunks" here). In JDK in-memory buffers, the buffer has to be expanded
* whenever it gets filled up. The old buffer's data is copied to the new one
* and the old one is discarded. In StreamCharBuffer, there are several ways to
* prevent unnecessary allocation & copy operations. The StreamCharBuffer
* prevent unnecessary allocation and copy operations. The StreamCharBuffer
* contains a linked list of different type of chunks: char arrays,
* java.lang.String chunks and other StreamCharBuffers as sub chunks. A
* StringChunk is appended to the linked list whenever a java.lang.String of a
Expand All @@ -101,13 +101,11 @@
*
* for example this line of code in a taglib would just append the buffer
* returned from the body closure evaluation to the buffer of the taglib:<br>
* <code>
* out << body()
* </code><br>
* <code>out &lt;&lt; body()</code>
* <br>
* other example:<br>
* <code>
* out << g.render(template: '/some/template', model:[somebean: somebean])
* </code><br>
* <code>out &lt;&lt; g.render(template: '/some/template', model:[somebean: somebean])</code>
* <br>
* There's no extra java.lang.String generation overhead.
*
* </p>
Expand All @@ -128,8 +126,8 @@
*
* <p>
* There's also several other options for reading data:<br>
* {@link #readAsCharArray()} reads the buffer to a char[] array<br>
* {@link #readAsString()} reads the buffer and wraps the char[] data as a
* readAsCharArray()reads the buffer to a char[] array<br>
* readAsString() reads the buffer and wraps the char[] data as a
* String<br>
* {@link #writeTo(Writer)} writes the buffer to a java.io.Writer<br>
* {@link #toCharArray()} returns the buffer as a char[] array, caches the
Expand All @@ -156,13 +154,13 @@
* <p>
* StreamCharBuffer keeps the buffer in a linked link of "chunks".<br>
* The main difference compared to JDK in-memory buffers (StringBuffer,
* StringBuilder & StringWriter) is that the buffer can be held in several
* StringBuilder and StringWriter) is that the buffer can be held in several
* smaller buffers ("chunks" here).<br>
* In JDK in-memory buffers, the buffer has to be expanded whenever it gets
* filled up. The old buffer's data is copied to the new one and the old one is
* discarded.<br>
* In StreamCharBuffer, there are several ways to prevent unnecessary allocation
* & copy operations.
* and copy operations.
* </p>
* <p>
* There can be several different type of chunks: char arrays (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
* Thrown if an unrecoverable problem occurs creating a DataBindingSource.
*
* @since 2.3
* @see org.grails.databinding.DataBindingSource
* @see grails.databinding.DataBindingSource
* @see DataBindingSourceCreator
*/
public class DataBindingSourceCreationException extends RuntimeException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ of this software and associated documentation files (the "Software"), to deal
* <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
* and if they are not the reserved words <code>true</code>,
* <code>false</code>, or <code>null</code>.</li>
* <li>Keys can be followed by <code>=</code> or <code>=></code> as well as
* <li>Keys can be followed by <code>=</code> or <code>=$gt;</code> as well as
* by <code>:</code>.</li>
* <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
* well as by <code>,</code> <small>(comma)</small>.</li>
Expand Down Expand Up @@ -796,7 +796,7 @@ public JSONObject putOpt(String key, Object value) throws JSONException {

/**
* Produce a string in double quotes with backslash sequences in all the
* right places. A backslash will be inserted within </, allowing JSON
* right places. A backslash will be inserted within &lt;/, allowing JSON
* text to be delivered in HTML. In JSON text, a string cannot contain a
* control character or an unescaped quote or backslash.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ public GrailsParameterMap getOriginalParams() {
}

/**
* Reset params by re-reading & initializing parameters from request
* Reset params by re-reading and initializing parameters from request
*/
public void resetParams() {
params = (GrailsParameterMap)getOriginalParams().clone();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ private void renderWithinGrailsWebRequest(Map<String, Object> model, HttpServlet
/**
* Renders a page with the specified TemplateEngine, mode and response.
* @param model The model to use
* @param request The HttpServletRequest
* @param response The HttpServletResponse instance
* @param engine The TemplateEngine to use
* @param webRequest The {@link org.grails.web.servlet.mvc.GrailsWebRequest}
* @param request The {@link javax.servlet.http.HttpServletRequest}
* @param response The {@link javax.servlet.http.HttpServletResponse} instance
*
* @throws java.io.IOException Thrown when an error occurs writing the response
*/
Expand Down

0 comments on commit c401faa

Please sign in to comment.