Skip to content

Commit

Permalink
Allow Thymeleaf to check for allowed method access of Java supertypes
Browse files Browse the repository at this point in the history
To prevent Thymeleaf templates from accessing methods on built-in Java types
that are not allowed, it ships with a list of allowed supertypes.
Its org.thymeleaf.util.ExpressionUtils type has an isMemberAllowed method
that calls getDeclaredMethods() on those types, so that needs to work for
all these types in a native image. Without this addition neither Spring
support (using SpEL expressions) nor OGNL-based expressions work correctly.

Recent versions of Thymeleaf have expanded the list with two additional
types, which is why the M2 version has 7 new entries while the RC1 / latest
has 9. Obviously the tests don't cover these.
For details, check out
https://github.com/thymeleaf/thymeleaf/blob/3.1-master/lib/thymeleaf/src/main/java/org/thymeleaf/util/ExpressionUtils.java
  • Loading branch information
jkuipers committed Apr 6, 2024
1 parent cd588e1 commit 318fc39
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 2 deletions.
49 changes: 49 additions & 0 deletions metadata/org.thymeleaf/thymeleaf/3.1.0.M2/reflect-config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,53 @@
[
{
"name": "java.util.Collection",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.lang.Iterable",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.util.List",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.util.Map",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.util.Map$Entry",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.util.Set",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.util.Calendar",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "org.thymeleaf.expression.Dates",
"allPublicMethods": true,
Expand Down
63 changes: 63 additions & 0 deletions metadata/org.thymeleaf/thymeleaf/3.1.0.RC1/reflect-config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,67 @@
[
{
"name": "java.util.Collection",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.lang.Iterable",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.util.Iterator",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.util.List",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.util.Map",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.util.Map$Entry",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.util.Set",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.util.Calendar",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "java.util.stream.Stream",
"allDeclaredMethods": true,
"condition": {
"typeReachable": "org.thymeleaf.util.ExpressionUtils"
}
},
{
"name": "org.thymeleaf.expression.Dates",
"allPublicMethods": true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.thymeleaf;

import org.junit.jupiter.api.Test;

import java.util.Calendar;
import java.util.Collections;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.thymeleaf.util.ExpressionUtils.isMemberAllowed;

/**
* Not including test for {@link java.util.Set} here, as it doesn't have any methods of its own.
*
* @see org.thymeleaf.util.ExpressionUtils#ALLOWED_JAVA_SUPERS
*/
public class ExpressionUtilsTest {

@Test
void allowsCollectionMethods() {
assertThat(isMemberAllowed(Collections.emptySet(), "isEmpty")).isTrue();
}

@Test
void allowsIterableMethods() {
assertThat(isMemberAllowed(Collections.emptyList(), "iterator")).isTrue();
}

@Test
void allowsListMethods() {
assertThat(isMemberAllowed(Collections.emptyList(), "get")).isTrue();
}

@Test
void allowsMapMethods() {
assertThat(isMemberAllowed(Collections.emptyMap(), "put")).isTrue();
}

@Test
void allowsMapEntryMethods() {
Map.Entry<String, String> entry = Collections.singletonMap("key", "value").entrySet().iterator().next();
assertThat(isMemberAllowed(entry, "getKey")).isTrue();
}

@Test
void allowsCalendarMethods() {
assertThat(isMemberAllowed(Calendar.getInstance(), "clear")).isTrue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ void renderDatesExpression() {
Context context = new Context();
Date date = new Date(81, 5, 15);
context.setVariable("date", date);
String template = "<p th:text=\"${#dates.format(date)}\"></p>";
String template = "<p th:text=\"${#dates.format(date, 'MMMM dd, YYYY')}\"></p>";
String output = templateEngine.process(template, context);
assertThat(output).startsWith("<p>June 15, 1981");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.thymeleaf;

import org.junit.jupiter.api.Test;

import java.util.Calendar;
import java.util.Collections;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.thymeleaf.util.ExpressionUtils.isMemberAllowed;

/**
* Not including test for {@link java.util.Set} here, as it doesn't have any methods of its own.
*
* @see org.thymeleaf.util.ExpressionUtils#ALLOWED_JAVA_SUPERS
*/
public class ExpressionUtilsTest {

@Test
void allowsCollectionMethods() {
assertThat(isMemberAllowed(Collections.emptySet(), "isEmpty")).isTrue();
}

@Test
void allowsIterableMethods() {
assertThat(isMemberAllowed(Collections.emptyList(), "iterator")).isTrue();
}

@Test
void allowsListMethods() {
assertThat(isMemberAllowed(Collections.emptyList(), "get")).isTrue();
}

@Test
void allowsMapMethods() {
assertThat(isMemberAllowed(Collections.emptyMap(), "put")).isTrue();
}

@Test
void allowsMapEntryMethods() {
Map.Entry<String, String> entry = Collections.singletonMap("key", "value").entrySet().iterator().next();
assertThat(isMemberAllowed(entry, "getKey")).isTrue();
}

@Test
void allowsCalendarMethods() {
assertThat(isMemberAllowed(Calendar.getInstance(), "clear")).isTrue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void renderDatesExpression() {
Context context = new Context();
Date date = new Date(81, 5, 15);
context.setVariable("date", date);
String template = "<p th:text=\"${#dates.format(date)}\"></p>";
String template = "<p th:text=\"${#dates.format(date, 'MMMM dd, YYYY')}\"></p>";
String output = templateEngine.process(template, context);
assertThat(output).startsWith("<p>June 15, 1981");
}
Expand Down

0 comments on commit 318fc39

Please sign in to comment.