diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextTest.java index 141b9aa4e3..85a311e30f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextTest.java @@ -208,6 +208,20 @@ public void testAddParameterNodeForFieldLevelConstraintCausesException() throws } } + @Test + public void testInjectionCausedByRecklessConcatenation() { + String maliciousPayload = "$\\A{1 + 1}"; + + // Simulate user entry, through a web form for example + MyObjectWithELInjectionRiskCausedByRecklessConcatenation object = new MyObjectWithELInjectionRiskCausedByRecklessConcatenation(); + object.field1 = maliciousPayload; + Set> constraintViolations = validator.validate( object ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( ValidationWithELInjectionRiskCausedByRecklessConcatenation.class ) + .withMessage( "Value '" + maliciousPayload + "' is invalid" ) + ); + } + @MyClassLevelValidation private static class MyObject { @NotNull @@ -278,6 +292,13 @@ public String getName() { } } + @ValidationWithELInjectionRiskCausedByRecklessConcatenation + private static class MyObjectWithELInjectionRiskCausedByRecklessConcatenation { + + String field1; + + } + @Retention(RUNTIME) @Constraint(validatedBy = MyClassLevelValidation.Validator.class) public @interface MyClassLevelValidation { @@ -486,4 +507,34 @@ public boolean isValid(String value, ConstraintValidatorContext context) { } } } + + @Retention(RUNTIME) + @Constraint(validatedBy = ValidationWithELInjectionRiskCausedByRecklessConcatenation.Validator.class) + public @interface ValidationWithELInjectionRiskCausedByRecklessConcatenation { + String message() default "failed"; + + Class[] groups() default { }; + + Class[] payload() default { }; + + class Validator + implements ConstraintValidator { + + @Override + public boolean isValid(MyObjectWithELInjectionRiskCausedByRecklessConcatenation value, ConstraintValidatorContext context) { + context.disableDefaultConstraintViolation(); + + // This is bad practice: message parameters should be used instead. + // Regardless, it can happen and should work as well as possible. + context.buildConstraintViolationWithTemplate( "Value '" + escape( value.field1 ) + "' is invalid" ) + .addConstraintViolation(); + + return false; + } + + private String escape(String value) { + return value.replaceAll( "\\$+\\{", "{" ); + } + } + } }