Skip to content

Commit

Permalink
Update SimpleDataBinder to prevent binding to specific types (#13269)
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
Puneet Behl committed Dec 6, 2023
1 parent 40b4c63 commit 74326bd
Show file tree
Hide file tree
Showing 11 changed files with 43 additions and 48 deletions.
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
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
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 @@ -762,7 +759,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 @@ -819,7 +816,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
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
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
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
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
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
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
Expand Up @@ -257,7 +257,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
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 74326bd

Please sign in to comment.