Override Compilation Customizer for Groovy Updater Scripts
Info: Available in brXM 14.7.13 and 15.2.0+
Overview
You can override the default Groovy compilation customizers for updater scripts to enforce custom security requirements. This allows you to control how Groovy scripts are compiled and which classes or packages they can access.
When to Use
Override the default compilation customizers if you need to:
- Enforce stricter security policies for Groovy updater scripts.
- Allow or restrict specific imports beyond the default configuration.
- Replace the default Bloomreach compilation customizers with project-specific logic.
Background
The Updater Editor in Bloomreach Content lets you create, manage, and execute Groovy updater scripts directly from the UI. Groovy supports compilation customizers, which modify the script compilation process.
Starting with versions 14.7.13 and 15.2.0, you can replace the default compilation customizers with your own implementation. Your customizers can access Bloomreach configuration through public static variables and methods in org.onehippo.repository.update.GroovyUpdaterClassLoader.
Important: Bloomreach supports the mechanism for customizing compilation, but does not support the customizers you implement. You are responsible for ensuring that your customizations do not introduce security vulnerabilities. When you provide your own CompilationCustomizerFactory, the default Bloomreach compilation customizers are no longer used.
Prerequisites
- A standard implementation project structure based on the Maven archetype.
- Familiarity with Java and Spring configuration.
- Replace
org.myprojectwith your actual package name.
Implementation Steps
To override the default Groovy compilation customizers:
-
Create the
spring.factoriesfileIn your
cmsmodule, add the following file:cms/src/main/resources/META-INF/spring.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfiguration = org.myproject.spring.MyAppConfiguration -
Implement the Spring configuration class
Create the class
org.myproject.spring.MyAppConfiguration:cms/src/main/java/org/myproject/spring/MyAppConfiguration.javapackage org.myproject.spring; import org.myproject.groovy.CompilationCustomizerFactoryImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyAppConfiguration { @Bean(initMethod = "init", destroyMethod = "destroy") public CompilationCustomizerFactoryImpl getCompilationCustomizerFactory() { return new CompilationCustomizerFactoryImpl(); } } -
Implement the custom compilation customizer factory
Create the class
org.myproject.groovy.CompilationCustomizerFactoryImpl:cms/src/main/java/org/myproject/groovy/CompilationCustomizerFactoryImpl.javapackage org.myproject.groovy; import static java.util.Collections.unmodifiableList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import org.codehaus.groovy.control.customizers.CompilationCustomizer; import org.onehippo.cms7.services.HippoServiceRegistry; import org.onehippo.repository.update.CompilationCustomizerFactory; public class CompilationCustomizerFactoryImpl implements CompilationCustomizerFactory { public static final List<String> myAllowedImports = unmodifiableList( Stream.of("org.onehippo.repository.update.BaseNodeUpdateVisitor", "javax.jcr.Node", "javax.jcr.RepositoryException", "javax.jcr.Session").collect(Collectors.toList())); public void init() { HippoServiceRegistry.register(this, CompilationCustomizerFactory.class); } public void destroy() { HippoServiceRegistry.unregister(this, CompilationCustomizerFactory.class); } @Override public CompilationCustomizer[] createCompilationCustomizers() { // TODO return your custom compilation customizers } } -
Implement the
createCompilationCustomizersmethodProvide your custom logic in the
createCompilationCustomizersmethod. See the example below for a reference implementation. -
Build and deploy your project
After implementing your customizer, build and deploy your project to apply the changes.
Example
The following example replaces the default implementation, which uses a list of disallowed imports, with a configuration that allows only specific imports.
package org.myproject.groovy; import static java.util.Collections.unmodifiableList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import org.codehaus.groovy.control.customizers.CompilationCustomizer; import org.codehaus.groovy.control.customizers.SecureASTCustomizer; import org.onehippo.cms7.services.HippoServiceRegistry; import org.onehippo.repository.update.CompilationCustomizerFactory; import org.onehippo.repository.update.GroovyUpdaterClassLoader; public class CompilationCustomizerFactoryImpl implements CompilationCustomizerFactory { public static final List<String> myAllowedImports = unmodifiableList( Stream.of("org.onehippo.repository.update.BaseNodeUpdateVisitor", "javax.jcr.Node", "javax.jcr.RepositoryException", "javax.jcr.Session").collect(Collectors.toList())); public void init() { HippoServiceRegistry.register(this, CompilationCustomizerFactory.class); } public void destroy() { HippoServiceRegistry.unregister(this, CompilationCustomizerFactory.class); } @Override public CompilationCustomizer[] createCompilationCustomizers() { final SecureASTCustomizer compilationCustomizer = new SecureASTCustomizer(); compilationCustomizer.setAllowedImports(myAllowedImports); compilationCustomizer.setIndirectImportCheckEnabled(true); compilationCustomizer.addExpressionCheckers(new GroovyUpdaterClassLoader.DefaultUpdaterExpressionChecker()); return new CompilationCustomizer[] { GroovyUpdaterClassLoader.createDefaultImportCustomizer(), compilationCustomizer }; } }
You can add additional classes to myAllowedImports as needed.
Verification
If a Groovy updater script imports a class that is not listed in myAllowedImports, the script will fail with an error similar to:
java.lang.SecurityException: Importing [my.package.MyClass] is not allowed