1818import java .io .IOException ;
1919import java .io .Serializable ;
2020import java .lang .reflect .Array ;
21+ import java .util .ArrayList ;
2122import java .util .HashMap ;
2223import java .util .List ;
2324import java .util .Map ;
2425import java .util .Set ;
2526
27+ import javax .lang .model .SourceVersion ;
28+
2629import org .apache .commons .logging .Log ;
2730import org .apache .commons .logging .LogFactory ;
31+
32+ import org .springframework .beans .BeanWrapperImpl ;
33+ import org .springframework .beans .PropertyAccessorUtils ;
2834import org .springframework .binding .convert .ConversionExecutor ;
2935import org .springframework .binding .convert .ConversionService ;
3036import org .springframework .binding .expression .EvaluationException ;
@@ -508,6 +514,7 @@ protected void addEmptyValueMapping(DefaultMapper mapper, String field, Object m
508514 protected void addDefaultMapping (DefaultMapper mapper , String parameter , Object model ) {
509515 Expression source = new RequestParameterExpression (parameter );
510516 ParserContext parserContext = new FluentParserContext ().evaluate (model .getClass ());
517+ validateDataBindingExpression (parameter , model );
511518 Expression target = expressionParser .parseExpression (parameter , parserContext );
512519 DefaultMapping mapping = new DefaultMapping (source , target );
513520 if (logger .isDebugEnabled ()) {
@@ -516,6 +523,49 @@ protected void addDefaultMapping(DefaultMapper mapper, String parameter, Object
516523 mapper .addMapping (mapping );
517524 }
518525
526+ /**
527+ * Check that the expression is a property path where each nested property
528+ * is a {@link SourceVersion#isName(CharSequence) valid Java identifier} and
529+ * that the first nested property name at least is a valid readable property
530+ * on the target object.
531+ */
532+ private void validateDataBindingExpression (String expression , Object model ) {
533+
534+ if (expressionParser instanceof BeanWrapperExpressionParser ) {
535+ return ;
536+ }
537+
538+ String errorMessage = "Invalid data binding expression: '" + expression + "' " +
539+ "for target model class '" + model .getClass () + "'" ;
540+
541+ List <String > propertyNames = new ArrayList <String >();
542+ while (true ) {
543+ int index = PropertyAccessorUtils .getFirstNestedPropertySeparatorIndex (expression );
544+ String nestedProperty = index != -1 ? expression .substring (0 , index ) : expression ;
545+ nestedProperty = PropertyAccessorUtils .getPropertyName (nestedProperty );
546+ propertyNames .add (nestedProperty );
547+ if (index == -1 ) {
548+ break ;
549+ }
550+ if (expression .length () == index + 1 ) {
551+ throw new IllegalStateException (errorMessage );
552+ }
553+ expression = expression .substring (index + 1 );
554+ }
555+
556+ BeanWrapperImpl beanWrapper = new BeanWrapperImpl (model );
557+ if (!beanWrapper .isReadableProperty (propertyNames .get (0 ))) {
558+ throw new IllegalStateException (errorMessage );
559+ }
560+
561+ for (int i =0 ; i < propertyNames .size (); i ++) {
562+ if (!SourceVersion .isName (propertyNames .get (i ))) {
563+ throw new IllegalStateException (errorMessage );
564+ }
565+ }
566+
567+ }
568+
519569 // package private
520570
521571 /**
0 commit comments