@@ -208,6 +208,20 @@ public void testAddParameterNodeForFieldLevelConstraintCausesException() throws
208208 }
209209 }
210210
211+ @ Test
212+ public void testInjectionCausedByRecklessConcatenation () {
213+ String maliciousPayload = "$\\ A{1 + 1}" ;
214+
215+ // Simulate user entry, through a web form for example
216+ MyObjectWithELInjectionRiskCausedByRecklessConcatenation object = new MyObjectWithELInjectionRiskCausedByRecklessConcatenation ();
217+ object .field1 = maliciousPayload ;
218+ Set <ConstraintViolation <MyObjectWithELInjectionRiskCausedByRecklessConcatenation >> constraintViolations = validator .validate ( object );
219+ assertThat ( constraintViolations ).containsOnlyViolations (
220+ violationOf ( ValidationWithELInjectionRiskCausedByRecklessConcatenation .class )
221+ .withMessage ( "Value '" + maliciousPayload + "' is invalid" )
222+ );
223+ }
224+
211225 @ MyClassLevelValidation
212226 private static class MyObject {
213227 @ NotNull
@@ -278,6 +292,13 @@ public String getName() {
278292 }
279293 }
280294
295+ @ ValidationWithELInjectionRiskCausedByRecklessConcatenation
296+ private static class MyObjectWithELInjectionRiskCausedByRecklessConcatenation {
297+
298+ String field1 ;
299+
300+ }
301+
281302 @ Retention (RUNTIME )
282303 @ Constraint (validatedBy = MyClassLevelValidation .Validator .class )
283304 public @interface MyClassLevelValidation {
@@ -486,4 +507,34 @@ public boolean isValid(String value, ConstraintValidatorContext context) {
486507 }
487508 }
488509 }
510+
511+ @ Retention (RUNTIME )
512+ @ Constraint (validatedBy = ValidationWithELInjectionRiskCausedByRecklessConcatenation .Validator .class )
513+ public @interface ValidationWithELInjectionRiskCausedByRecklessConcatenation {
514+ String message () default "failed" ;
515+
516+ Class <?>[] groups () default { };
517+
518+ Class <? extends Payload >[] payload () default { };
519+
520+ class Validator
521+ implements ConstraintValidator <ValidationWithELInjectionRiskCausedByRecklessConcatenation , MyObjectWithELInjectionRiskCausedByRecklessConcatenation > {
522+
523+ @ Override
524+ public boolean isValid (MyObjectWithELInjectionRiskCausedByRecklessConcatenation value , ConstraintValidatorContext context ) {
525+ context .disableDefaultConstraintViolation ();
526+
527+ // This is bad practice: message parameters should be used instead.
528+ // Regardless, it can happen and should work as well as possible.
529+ context .buildConstraintViolationWithTemplate ( "Value '" + escape ( value .field1 ) + "' is invalid" )
530+ .addConstraintViolation ();
531+
532+ return false ;
533+ }
534+
535+ private String escape (String value ) {
536+ return value .replaceAll ( "\\ $+\\ {" , "{" );
537+ }
538+ }
539+ }
489540}
0 commit comments