Skip to content

Commit aab8c1f

Browse files
committed
Catch AssertionError and NoClassDefFoundError in groovy scripts
Previously, we only caught subclasses of Exception, however, there are some cases when Errors are thrown instead of Exceptions. These two cases are `assert` and when a class cannot be found. Without this change, the exception would bubble up to the `uncaughtExceptionHandler`, which would in turn, exit the JVM (related: elastic#19923). A note of difference between regular Java asserts and Groovy asserts, from http://docs.groovy-lang.org/docs/latest/html/documentation/core-testing-guide.html "Another important difference from Java is that in Groovy assertions are enabled by default. It has been a language design decision to remove the possibility to deactivate assertions." In the event that a user uses an assert such as: ```groovy def bar=false; assert bar, "message"; ``` The GroovyScriptEngineService throws a NoClassDefFoundError being unable to find the `java.lang.StringBuffer` class. It is *highly* recommended that any Groovy scripting user switch to using regular exceptions rather than unconfiguration Groovy assertions. Resolves elastic#19806
1 parent d7dbb2b commit aab8c1f

File tree

2 files changed

+20
-4
lines changed

2 files changed

+20
-4
lines changed

modules/lang-groovy/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,10 +293,19 @@ public Object run() {
293293
// NOTE: we truncate the stack because IndyInterface has security issue (needs getClassLoader)
294294
// we don't do a security check just as a tradeoff, it cannot really escalate to anything.
295295
return AccessController.doPrivileged((PrivilegedAction<Object>) script::run);
296-
} catch (Exception e) {
297-
if (logger.isTraceEnabled()) {
298-
logger.trace("failed to run {}", e, compiledScript);
296+
} catch (AssertionError ae) {
297+
// Groovy asserts are not java asserts, and cannot be disabled, so we do a best-effort trying to determine if this is a
298+
// Groovy assert (in which case we wrap it and throw), or a real Java assert, in which case we rethrow it as-is, likely
299+
// resulting in the uncaughtExceptionHandler handling it.
300+
final StackTraceElement[] elements = ae.getStackTrace();
301+
if (elements.length > 0 && "org.codehaus.groovy.runtime.InvokerHelper".equals(elements[0].getClassName())) {
302+
logger.trace("failed to run {}", ae, compiledScript);
303+
throw new ScriptException("Error evaluating " + compiledScript.name(),
304+
ae, emptyList(), "", compiledScript.lang());
299305
}
306+
throw ae;
307+
} catch (Exception | NoClassDefFoundError e) {
308+
logger.trace("failed to run {}", e, compiledScript);
300309
throw new ScriptException("Error evaluating " + compiledScript.name(), e, emptyList(), "", compiledScript.lang());
301310
}
302311
}

modules/lang-groovy/src/test/java/org/elasticsearch/script/groovy/GroovySecurityTests.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@ public void testEvilGroovyScripts() throws Exception {
123123
}
124124
}
125125

126+
public void testGroovyScriptsThatThrowErrors() throws Exception {
127+
assertFailure("assert false, \"msg\";", AssertionError.class);
128+
assertFailure("def foo=false; assert foo;", AssertionError.class);
129+
// Groovy's asserts require org.codehaus.groovy.runtime.InvokerHelper, so they are denied
130+
assertFailure("def foo=false; assert foo, \"msg2\";", NoClassDefFoundError.class);
131+
}
132+
126133
/** runs a script */
127134
private void doTest(String script) {
128135
Map<String, Object> vars = new HashMap<String, Object>();
@@ -146,7 +153,7 @@ private void assertSuccess(String script) {
146153
doTest(script);
147154
}
148155

149-
/** asserts that a script triggers securityexception */
156+
/** asserts that a script triggers the given exceptionclass */
150157
private void assertFailure(String script, Class<? extends Throwable> exceptionClass) {
151158
try {
152159
doTest(script);

0 commit comments

Comments
 (0)