Skip to content

Commit

Permalink
feat(perf): adapt policy for new classloader system
Browse files Browse the repository at this point in the history
  • Loading branch information
jhaeyaert authored and brasseld committed Jan 6, 2022
1 parent fda3132 commit 08c3aea
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 30 deletions.
8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<groovy.version>3.0.9</groovy.version>
<groovy-sandbox.version>1.27</groovy-sandbox.version>
<commons-lang3.version>3.11</commons-lang3.version>
<guava.version>30.1.1-jre</guava.version>
<maven-assembly-plugin.version>2.5.5</maven-assembly-plugin.version>
</properties>

Expand Down Expand Up @@ -123,6 +124,13 @@
<scope>provided</scope>
</dependency>

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
<scope>provided</scope>
</dependency>

<!-- Test scope -->
<dependency>
<groupId>junit</groupId>
Expand Down
10 changes: 8 additions & 2 deletions src/main/java/io/gravitee/policy/groovy/GroovyInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,25 @@
public class GroovyInitializer implements PolicyContext, PolicyContextProviderAware {

private Environment environment;
private boolean classLoaderLegacyMode = true;

@Override
public void onActivation() throws Exception {
SecuredResolver.initialize(this.environment);
if (classLoaderLegacyMode || !SecuredResolver.isInitialized()) {
SecuredResolver.initialize(this.environment);
}
}

@Override
public void onDeactivation() throws Exception {
SecuredResolver.destroy();
if (classLoaderLegacyMode) {
SecuredResolver.destroy();
}
}

@Override
public void setPolicyContextProvider(PolicyContextProvider policyContextProvider) {
this.environment = policyContextProvider.getComponent(Environment.class);
this.classLoaderLegacyMode = environment.getProperty("classloader.legacy.enabled", Boolean.class, true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,18 @@
*/
package io.gravitee.policy.groovy.sandbox;

import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import groovy.lang.Binding;
import groovy.lang.GroovyCodeSource;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import groovy.transform.ThreadInterrupt;
import groovy.transform.TimedInterrupt;
import io.gravitee.policy.groovy.GroovyPolicy;
import io.gravitee.policy.groovy.utils.Sha1;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.time.Duration;
import org.apache.groovy.json.internal.FastStringUtils;
import org.apache.groovy.util.Maps;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer;
import org.codehaus.groovy.control.customizers.SecureASTCustomizer;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.kohsuke.groovy.sandbox.GroovyInterceptor;
Expand All @@ -44,6 +37,12 @@
* @author GraviteeSource Team
*/
public class SecuredGroovyShell {

/**
* Number of hours to keep compiled script in cache after the last time it was accessed.
*/
private static final int CODE_CACHE_EXPIRATION_HOURS = 1;

static {
// Do not change this block of code which is required to work with groovy 2.5 and the classloader used
// to load services
Expand All @@ -53,11 +52,13 @@ public class SecuredGroovyShell {
Thread.currentThread().setContextClassLoader(loader);
}

private GroovyShell groovyShell;
private ConcurrentMap<String, Class<?>> sources = new ConcurrentHashMap<>();
private GroovyInterceptor groovyInterceptor;
private final GroovyShell groovyShell;
private final Cache<String, Class<?>> sources;
private final GroovyInterceptor groovyInterceptor;

public SecuredGroovyShell() {
this.sources = CacheBuilder.newBuilder().expireAfterAccess(Duration.ofHours(CODE_CACHE_EXPIRATION_HOURS)).build();

CompilerConfiguration conf = new CompilerConfiguration();

// Add Kohsuke's sandbox transformer which will delegate calls to SecuredInterceptor.
Expand Down Expand Up @@ -101,12 +102,22 @@ public <T> T evaluate(String script, Binding binding) {
private Class<?> getOrCreate(String script) throws CompilationFailedException {
String key = Sha1.sha1(script);

return sources.computeIfAbsent(
key,
s -> {
GroovyCodeSource gcs = new GroovyCodeSource(script, key, GroovyShell.DEFAULT_CODE_BASE);
return groovyShell.getClassLoader().parseClass(gcs, true);
try {
return sources.get(
key,
() -> {
GroovyCodeSource gcs = new GroovyCodeSource(script, key, GroovyShell.DEFAULT_CODE_BASE);
return groovyShell.getClassLoader().parseClass(gcs, true);
}
);
} catch (Exception e) {
final Throwable cause = e.getCause();
if (cause instanceof CompilationFailedException) {
throw (CompilationFailedException) cause;
} else if (cause instanceof SecurityException) {
throw (SecurityException) cause;
}
);
throw new RuntimeException("Unable to compile script", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,19 @@ public class SecuredResolver {
private final Map<Class<?>, List<Method>> methodsByTypeAndSuperTypes;
private final Map<String, Boolean> resolved;

public static void initialize(@Nullable Environment environment) {
loadWhitelist(environment);
INSTANCE = new SecuredResolver();
public static synchronized void initialize(@Nullable Environment environment) {
if (!isInitialized()) {
loadWhitelist(environment);
INSTANCE = new SecuredResolver();
}
}

public static synchronized boolean isInitialized() {
return INSTANCE != null;
}

public static void destroy() {
if (INSTANCE != null) {
public static synchronized void destroy() {
if (isInitialized()) {
methodsByType.clear();
fieldsByType.clear();
constructorsByType.clear();
Expand All @@ -123,7 +129,7 @@ public static void destroy() {
}

public static SecuredResolver getInstance() {
if (INSTANCE == null) {
if (!isInitialized()) {
initialize(null);
}

Expand Down
5 changes: 2 additions & 3 deletions src/main/java/io/gravitee/policy/groovy/utils/Sha1.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,12 @@ private static String convertToHex(byte[] data) {
public static String sha1(String text) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] sha1hash = new byte[40];
byte[] sha1hash;
md.update(text.getBytes("UTF-8"), 0, text.length());
sha1hash = md.digest();
return convertToHex(sha1hash);
} catch (Exception ex) {
ex.printStackTrace();
return null;
throw new RuntimeException(ex);
}
}
}

0 comments on commit 08c3aea

Please sign in to comment.