mirror of https://github.com/apache/lucene.git
SOLR-14110: sandbox javax.script usage in tests
This commit is contained in:
parent
71a5714e29
commit
612cba38ca
|
@ -19,6 +19,12 @@ package org.apache.solr.handler.dataimport;
|
||||||
import static org.apache.solr.handler.dataimport.DataImportHandlerException.wrapAndThrow;
|
import static org.apache.solr.handler.dataimport.DataImportHandlerException.wrapAndThrow;
|
||||||
import static org.apache.solr.handler.dataimport.DataImportHandlerException.SEVERE;
|
import static org.apache.solr.handler.dataimport.DataImportHandlerException.SEVERE;
|
||||||
|
|
||||||
|
import java.security.AccessControlContext;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
|
import java.security.PrivilegedActionException;
|
||||||
|
import java.security.PrivilegedExceptionAction;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.script.Invocable;
|
import javax.script.Invocable;
|
||||||
|
@ -46,7 +52,16 @@ public class ScriptTransformer extends Transformer {
|
||||||
private String functionName;
|
private String functionName;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object transformRow(Map<String, Object> row, Context context) {
|
public Object transformRow(Map<String,Object> row, Context context) {
|
||||||
|
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
|
||||||
|
@Override
|
||||||
|
public Object run() {
|
||||||
|
return transformRowUnsafe(row, context);
|
||||||
|
}
|
||||||
|
}, SCRIPT_SANDBOX);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object transformRowUnsafe(Map<String, Object> row, Context context) {
|
||||||
try {
|
try {
|
||||||
if (engine == null)
|
if (engine == null)
|
||||||
initEngine(context);
|
initEngine(context);
|
||||||
|
@ -84,7 +99,17 @@ public class ScriptTransformer extends Transformer {
|
||||||
+ scriptEngine.getClass().getName());
|
+ scriptEngine.getClass().getName());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
try {
|
||||||
|
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void run() throws ScriptException {
|
||||||
scriptEngine.eval(scriptText);
|
scriptEngine.eval(scriptText);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, SCRIPT_SANDBOX);
|
||||||
|
} catch (PrivilegedActionException e) {
|
||||||
|
throw (ScriptException) e.getException();
|
||||||
|
}
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
wrapAndThrow(SEVERE, e, "'eval' failed with language: " + scriptLang
|
wrapAndThrow(SEVERE, e, "'eval' failed with language: " + scriptLang
|
||||||
+ " and script: \n" + scriptText);
|
+ " and script: \n" + scriptText);
|
||||||
|
@ -99,4 +124,8 @@ public class ScriptTransformer extends Transformer {
|
||||||
return functionName;
|
return functionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sandbox for script code: zero permissions
|
||||||
|
private static final AccessControlContext SCRIPT_SANDBOX =
|
||||||
|
new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, null) });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,28 @@ public class TestScriptTransformer extends AbstractDataImportHandlerTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEvil() {
|
||||||
|
assumeTrue("This test only works with security manager", System.getSecurityManager() != null);
|
||||||
|
String script = "function f1(row) {"
|
||||||
|
+ "var os = Packages.java.lang.System.getProperty('os.name');"
|
||||||
|
+ "row.put('name', os);"
|
||||||
|
+ "return row;\n"
|
||||||
|
+ "}";
|
||||||
|
|
||||||
|
Context context = getContext("f1", script);
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("name", "Scott");
|
||||||
|
EntityProcessorWrapper sep = new EntityProcessorWrapper(new SqlEntityProcessor(), null, null);
|
||||||
|
sep.init(context);
|
||||||
|
DataImportHandlerException expected = expectThrows(DataImportHandlerException.class, () -> {
|
||||||
|
sep.applyTransformer(map);
|
||||||
|
});
|
||||||
|
assumeFalse("This JVM does not have JavaScript installed. Test Skipped.",
|
||||||
|
expected.getMessage().startsWith("Cannot load Script Engine for language"));
|
||||||
|
assertTrue(expected.getCause().toString(), SecurityException.class.isAssignableFrom(expected.getCause().getClass()));
|
||||||
|
}
|
||||||
|
|
||||||
private Context getContext(String funcName, String script) {
|
private Context getContext(String funcName, String script) {
|
||||||
List<Map<String, String>> fields = new ArrayList<>();
|
List<Map<String, String>> fields = new ArrayList<>();
|
||||||
Map<String, String> entity = new HashMap<>();
|
Map<String, String> entity = new HashMap<>();
|
||||||
|
|
|
@ -41,6 +41,12 @@ import java.io.InputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.AccessControlContext;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
|
import java.security.PrivilegedActionException;
|
||||||
|
import java.security.PrivilegedExceptionAction;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -269,7 +275,7 @@ public class StatelessScriptUpdateProcessorFactory extends UpdateRequestProcesso
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ScriptFile scriptFile : scriptFiles) {
|
for (ScriptFile scriptFile : scriptFiles) {
|
||||||
ScriptEngine engine = null;
|
final ScriptEngine engine;
|
||||||
if (null != engineName) {
|
if (null != engineName) {
|
||||||
engine = scriptEngineManager.getEngineByName(engineName);
|
engine = scriptEngineManager.getEngineByName(engineName);
|
||||||
if (engine == null) {
|
if (engine == null) {
|
||||||
|
@ -314,7 +320,17 @@ public class StatelessScriptUpdateProcessorFactory extends UpdateRequestProcesso
|
||||||
Reader scriptSrc = scriptFile.openReader(resourceLoader);
|
Reader scriptSrc = scriptFile.openReader(resourceLoader);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
try {
|
||||||
|
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void run() throws ScriptException {
|
||||||
engine.eval(scriptSrc);
|
engine.eval(scriptSrc);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, SCRIPT_SANDBOX);
|
||||||
|
} catch (PrivilegedActionException e) {
|
||||||
|
throw (ScriptException) e.getException();
|
||||||
|
}
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
||||||
"Unable to evaluate script: " +
|
"Unable to evaluate script: " +
|
||||||
|
@ -424,6 +440,15 @@ public class StatelessScriptUpdateProcessorFactory extends UpdateRequestProcesso
|
||||||
* cast to a java Boolean.
|
* cast to a java Boolean.
|
||||||
*/
|
*/
|
||||||
private boolean invokeFunction(String name, Object... cmd) {
|
private boolean invokeFunction(String name, Object... cmd) {
|
||||||
|
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public Boolean run() {
|
||||||
|
return invokeFunctionUnsafe(name, cmd);
|
||||||
|
}
|
||||||
|
}, SCRIPT_SANDBOX);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean invokeFunctionUnsafe(String name, Object... cmd) {
|
||||||
|
|
||||||
for (EngineInfo engine : engines) {
|
for (EngineInfo engine : engines) {
|
||||||
try {
|
try {
|
||||||
|
@ -496,4 +521,8 @@ public class StatelessScriptUpdateProcessorFactory extends UpdateRequestProcesso
|
||||||
(input, StandardCharsets.UTF_8);
|
(input, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sandbox for script code: zero permissions
|
||||||
|
private static final AccessControlContext SCRIPT_SANDBOX =
|
||||||
|
new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, null) });
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
var sys = Packages.java.lang.System;
|
||||||
|
|
||||||
|
function processAdd(cmd) {
|
||||||
|
sys.getProperty("os.name");
|
||||||
|
}
|
||||||
|
|
||||||
|
function processDelete(cmd) {
|
||||||
|
}
|
||||||
|
|
||||||
|
function processMergeIndexes(cmd) {
|
||||||
|
}
|
||||||
|
|
||||||
|
function processCommit(cmd) {
|
||||||
|
}
|
||||||
|
|
||||||
|
function processRollback(cmd) {
|
||||||
|
}
|
||||||
|
|
||||||
|
function finish() {
|
||||||
|
}
|
||||||
|
|
|
@ -117,4 +117,10 @@
|
||||||
</processor>
|
</processor>
|
||||||
</updateRequestProcessorChain>
|
</updateRequestProcessorChain>
|
||||||
|
|
||||||
|
<updateRequestProcessorChain name="evil">
|
||||||
|
<processor class="solr.StatelessScriptUpdateProcessorFactory">
|
||||||
|
<str name="script">evil.js</str>
|
||||||
|
</processor>
|
||||||
|
</updateRequestProcessorChain>
|
||||||
|
|
||||||
</config>
|
</config>
|
||||||
|
|
|
@ -259,4 +259,14 @@ public class StatelessScriptUpdateProcessorFactoryTest extends UpdateProcessorTe
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testScriptSandbox() throws Exception {
|
||||||
|
assumeTrue("This test only works with security manager", System.getSecurityManager() != null);
|
||||||
|
expectThrows(SecurityException.class, () -> {
|
||||||
|
processAdd("evil",
|
||||||
|
doc(f("id", "5"),
|
||||||
|
f("name", " foo "),
|
||||||
|
f("subject", "BAR")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue