thymeleaf: Release 3.0.12 has broken most of our templates due to #809

Due to changes in https://github.com/thymeleaf/thymeleaf/issues/809 we have a lot of templates that are now broken.

For a lot of our templates we use static members in our Spring controllers for the request names. Hrefs within our templates look like the following:

<a th:href="@{${T(com.test.controller.DashboardController).REQUEST_DASHBOARD}}">

This now results in the following exception:

org.thymeleaf.exceptions.TemplateProcessingException: Instantiation of new objects and access to static classes is forbidden in this context

This pattern is used in 100’s of templates in our code base. We obviously don’t want to completely disable the restricted expression evaluation mode, but is there a way to turn off the restriction of just the static class access?

Thanks, Scott

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 36
  • Comments: 20 (1 by maintainers)

Commits related to this issue

Most upvoted comments

+1 Many of my templates are broken due to this “minor”?? update. It looks like a breaking change to me and should not have been embedded within a minor version update…

@danielfernandez can you comment on this issue? Seems to be a major problem for many people

I absolutely understand the rationale behind the changes, and we’re more than happy to adjust our code to access enums in a safer way, but I have to confess “Go add a getter to a model object for every enum you might ever want to use in a template” is not the answer for which I was hoping.

It would be nice if thymeleaf could provide a recommended way of managing enum visibility - possibly even passing in a list of enums to make available in some form to the templates.

I really wonder why there is no mention of the alternatives. I ended up using with th:with like below

th:with="rptBy=${T(com.test.enum.ReportBy).BY_G1}"
th:data-groupby="${rptBy}"

As the intent of the restricted expression evaluation mode is to:

reduce the risks of code injection as much as possible. Thymeleaf does this by preventing the use of direct input from users in certain parts of the template. This direct input from users refers to request parameters, as these might not have passed a validation process at the controller. (source)

IMHO restricting access to constants and enums doesn’t make sense to achieve this goal, as they cannot be manipulated by user input.

@danielfernandez as you are responsible for #809 I would like to know what the suggested replacement for accessing constants is?

Would it be possible for Thymeleaf to determine that expressions reference constants and allow those?.

Here too. Most of our template broke with 3.0.12, which is part of spring-boot-starter-thymeleaf-2.4.2. Thus atm we have to downgrade thymeleaf explicit…

A version bump would have been nice.

I really wonder why there is no mention of the alternatives. I ended up using with th:with like below

th:with="rptBy=${T(com.test.enum.ReportBy).BY_G1}"
th:data-groupby="${rptBy}"

I can encourage you using this quickfix for your broken definitions. I only had to use a bunch of it, and can now work again with the most recent version again.

This is my solution, it worked, no need to modify template.

    @Bean
    public TemplateDialect templateDialect() {
        return new TemplateDialect();
    }
class TemplateDialect extends AbstractDialect implements IExpressionObjectDialect {

    public TemplateDialect() {
        super(TemplateDialect.class.getName());
    }

    @Override
    public IExpressionObjectFactory getExpressionObjectFactory() {
        return new IExpressionObjectFactory() {

            @Override
            public Set<String> getAllExpressionObjectNames() {
                Set<String> set = new LinkedHashSet<>();
                set.add("request");
                set.add("response");
                set.add("session");
                set.add("servletContext");
                return set;
            }

            @Override
            public Object buildObject(IExpressionContext context, String expressionObjectName) {
                if ("request".equals(expressionObjectName)) {
                    return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                }
                if ("response".equals(expressionObjectName)) {
                    return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
                }
                if ("session".equals(expressionObjectName)) {
                    return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession(false);
                }
                if ("servletContext".equals(expressionObjectName)) {
                    return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getServletContext();
                }
                throw new UnsupportedOperationException("unsupported expression");
            }

            @Override
            public boolean isCacheable(String expressionObjectName) {
                return true;
            }
        };
    }
}


I complete understand the idea of preventing execution of arbitrary code, but in most of the cases myself and others have raised, we’re talking about constants/enums. Could code be added to the detection routine to determine if the target of the T() expression is a constant and allow that?

I had the same issue on my spring boot project(2.4.4), maybe 30+ templates affected, so spend few hours refactoring them into model attributes.

Could you maybe give examples on which expressions have to be formatted in a new way? That would probably help the majority of us to get rid of the old syntax more easily.