All Downloads are FREE. Search and download functionalities are using the official Maven repository.

delight.nashornsandbox.internal.NashornSandboxImpl Maven / Gradle / Ivy

package delight.nashornsandbox.internal;

import java.io.Writer;
import java.util.concurrent.ExecutorService;

import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import delight.nashornsandbox.NashornSandbox;
import delight.nashornsandbox.exceptions.ScriptCPUAbuseException;
import delight.nashornsandbox.exceptions.ScriptMemoryAbuseException;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;

/**
 * Nashorn sandbox implementation.
 *
 * 

* Created on 2015-08-07 *

* * @author mxro * @author Marco Ellwanger * @author Youness SAHOUANE * @author Eduardo Velasques * @author philipborg * @author Marcin Golebski * @version $Id$ */ @SuppressWarnings("restriction") public class NashornSandboxImpl implements NashornSandbox { static final Logger LOG = LoggerFactory.getLogger(NashornSandbox.class); protected final SandboxClassFilter sandboxClassFilter; protected final ScriptEngine scriptEngine; /** Maximum CPU time in miliseconds. */ protected long maxCPUTime = 0L; /** Maximum memory of executor thread used. */ protected long maxMemory = 0L; protected ExecutorService executor; protected boolean allowPrintFunctions = false; protected boolean allowReadFunctions = false; protected boolean allowLoadFunctions = false; protected boolean allowExitFunctions = false; protected boolean allowGlobalsObjects = false; protected boolean allowNoBraces = false; protected JsEvaluator evaluator; protected JsSanitizer sanitizer; protected boolean engineAsserted; /** The size of the LRU cache of prepared statemensts. */ protected int maxPreparedStatements; public NashornSandboxImpl() { this.maxPreparedStatements = 0; this.sandboxClassFilter = new SandboxClassFilter(); final NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); this.scriptEngine = factory.getScriptEngine(this.sandboxClassFilter); this.allow(InterruptTest.class); } private void assertScriptEngine() { try { final StringBuilder sb = new StringBuilder(); if (!allowExitFunctions) { sb.append("quit=function(){};exit=function(){};"); } if (!allowPrintFunctions) { sb.append("print=function(){};echo = function(){};"); } if (!allowReadFunctions) { sb.append("readFully=function(){};").append("readLine=function(){};"); } if (!allowLoadFunctions) { sb.append("load=function(){};loadWithNewGlobal=function(){};"); } if (!allowGlobalsObjects) { sb.append("$ARG=null;$ENV=null;$EXEC=null;"); sb.append("$OPTIONS=null;$OUT=null;$ERR=null;$EXIT=null;"); } scriptEngine.eval(sb.toString()); } catch (final Exception e) { throw new RuntimeException(e); } } @Override public Object eval(final String js) throws ScriptCPUAbuseException, ScriptException { return eval(js, null); } @Override public Object eval(final String js, final ScriptContext scriptContext) throws ScriptCPUAbuseException, ScriptException { if (!engineAsserted) { engineAsserted = true; assertScriptEngine(); } try { if (maxCPUTime == 0 && maxMemory == 0) { if (LOG.isDebugEnabled()) { LOG.debug("--- Running JS ---"); LOG.debug(js); LOG.debug("--- JS END ---"); } if (scriptContext != null) { return scriptEngine.eval(js, scriptContext); } return this.scriptEngine.eval(js); } checkExecutorPresence(); final JsSanitizer sanitizer = getSanitizer(); final String securedJs = sanitizer.secureJs(js); final JsEvaluator evaluator = getEvaluator(); evaluator.setJs(securedJs); evaluator.setScriptContext(scriptContext); executor.execute(evaluator); evaluator.runMonitor(); if (evaluator.isCPULimitExceeded()) { throw new ScriptCPUAbuseException( "Script used more than the allowed [" + maxCPUTime + " ms] of CPU time.", evaluator.isScriptKilled(), evaluator.getException()); } else if (evaluator.isMemoryLimitExceeded()) { throw new ScriptMemoryAbuseException( "Script used more than the allowed [" + maxMemory + " B] of memory.", evaluator.isScriptKilled(), evaluator.getException()); } if (evaluator.getException() != null) { throw evaluator.getException(); } return evaluator.getResult(); } catch (RuntimeException | ScriptException e) { throw e; } catch (final Exception e) { throw new RuntimeException(e); } } private JsEvaluator getEvaluator() { return new JsEvaluator(scriptEngine, maxCPUTime, maxMemory); } private void checkExecutorPresence() { if (executor == null) { throw new IllegalStateException("When a CPU time or memory limit is set, an executor " + "needs to be provided by calling .setExecutor(...)"); } } @Override public void setMaxCPUTime(final long limit) { maxCPUTime = limit; } @Override public void setMaxMemory(final long limit) { maxMemory = limit; } JsSanitizer getSanitizer() { if (sanitizer == null) { sanitizer = new JsSanitizer(scriptEngine, maxPreparedStatements, allowNoBraces); } return sanitizer; } @Override public void allow(final Class clazz) { sandboxClassFilter.add(clazz); } @Override public void disallow(final Class clazz) { sandboxClassFilter.remove(clazz); } @Override public boolean isAllowed(final Class clazz) { return sandboxClassFilter.contains(clazz); } @Override public void disallowAllClasses() { sandboxClassFilter.clear(); } @Override public void inject(final String variableName, final Object object) { if (object != null && !sandboxClassFilter.contains(object.getClass())) { allow(object.getClass()); } scriptEngine.put(variableName, object); } @Override public void setExecutor(final ExecutorService executor) { this.executor = executor; } @Override public ExecutorService getExecutor() { return executor; } @Override public Object get(final String variableName) { return scriptEngine.get(variableName); } @Override public void allowPrintFunctions(final boolean v) { if (engineAsserted) { throw new IllegalStateException("Please set this property before calling eval."); } allowPrintFunctions = v; } @Override public void allowReadFunctions(final boolean v) { if (engineAsserted) { throw new IllegalStateException("Please set this property before calling eval."); } allowReadFunctions = v; } @Override public void allowLoadFunctions(final boolean v) { if (engineAsserted) { throw new IllegalStateException("Please set this property before calling eval."); } allowLoadFunctions = v; } @Override public void allowExitFunctions(final boolean v) { if (engineAsserted) { throw new IllegalStateException("Please set this property before calling eval."); } allowExitFunctions = v; } @Override public void allowGlobalsObjects(final boolean v) { if (engineAsserted) { throw new IllegalStateException("Please set this property before calling eval."); } allowGlobalsObjects = v; } @Override public void allowNoBraces(final boolean v) { if (allowNoBraces != v) { sanitizer = null; } allowNoBraces = v; } @Override public void setWriter(final Writer writer) { scriptEngine.getContext().setWriter(writer); } @Override public void setMaxPerparedStatements(final int max) { if (maxPreparedStatements != max) { sanitizer = null; } maxPreparedStatements = max; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy