/* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.activiti.scripting.secure.impl; import org.mozilla.javascript.Callable; import org.mozilla.javascript.Context; import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.Scriptable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Joram Barrez */ public class SecureScriptContextFactory extends ContextFactory { private static final Logger LOGGER = LoggerFactory.getLogger(SecureScriptContextFactory.class); protected SecureScriptClassShutter classShutter; protected int observeInstructionCount = 10; protected long maxScriptExecutionTime = -1L; protected long maxMemoryUsed = -1L; protected int maxStackDepth = -1; protected int optimizationLevel = -1; protected SecureScriptThreadMxBeanWrapper threadMxBeanWrapper; protected Context makeContext() { SecureScriptContext context = new SecureScriptContext(); // Setting this, as otherwise variables set seem to have the wrong type context.getWrapFactory().setJavaPrimitiveWrap(false); context.setOptimizationLevel(optimizationLevel); // Class white-listing if (classShutter != null) { context.setClassShutter(classShutter); } // Needed for both time and memory measurement if (maxScriptExecutionTime > 0L || maxMemoryUsed > 0L) { context.setGenerateObserverCount(true); context.setInstructionObserverThreshold(observeInstructionCount); } // Memory limit if (maxMemoryUsed > 0) { context.setThreadId(Thread.currentThread().getId()); } // Max stack depth if (maxStackDepth > 0) { context.setOptimizationLevel(-1); // stack depth can only be set when no optimizations are applied context.setMaximumInterpreterStackDepth(maxStackDepth); } return context; } protected void observeInstructionCount(Context cx, int instructionCount) { SecureScriptContext context = (SecureScriptContext) cx; // Time limit if (maxScriptExecutionTime > 0) { long currentTime = System.currentTimeMillis(); if (currentTime - context.getStartTime() > maxScriptExecutionTime) { throw new Error("Maximum variableScope time of " + maxScriptExecutionTime + " ms exceeded"); } } // Memory if (maxMemoryUsed > 0 && threadMxBeanWrapper != null) { if (context.getStartMemory() <= 0) { context.setStartMemory(threadMxBeanWrapper.getThreadAllocatedBytes(context.getThreadId())); } else { long currentAllocatedBytes = threadMxBeanWrapper.getThreadAllocatedBytes(context.getThreadId()); if (currentAllocatedBytes - context.getStartMemory() >= maxMemoryUsed) { throw new Error("Memory limit of " + maxMemoryUsed + " bytes reached"); } } } } // Override {@link #doTopCall(Callable, Context, Scriptable, Scriptable, Object[])} protected Object doTopCall(Callable callable, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { SecureScriptContext mcx = (SecureScriptContext) cx; mcx.setStartTime(System.currentTimeMillis()); return super.doTopCall(callable, cx, scope, thisObj, args); } public int getOptimizationLevel() { return optimizationLevel; } public void setOptimizationLevel(int optimizationLevel) { this.optimizationLevel = optimizationLevel; } public SecureScriptClassShutter getClassShutter() { return classShutter; } public void setClassShutter(SecureScriptClassShutter classShutter) { this.classShutter = classShutter; } public int getObserveInstructionCount() { return observeInstructionCount; } public void setObserveInstructionCount(int observeInstructionCount) { this.observeInstructionCount = observeInstructionCount; } public long getMaxScriptExecutionTime() { return maxScriptExecutionTime; } public void setMaxScriptExecutionTime(long maxScriptExecutionTime) { this.maxScriptExecutionTime = maxScriptExecutionTime; } public long getMaxMemoryUsed() { return maxMemoryUsed; } public void setMaxMemoryUsed(long maxMemoryUsed) { this.maxMemoryUsed = maxMemoryUsed; if (maxMemoryUsed > 0) { try { Class clazz = Class.forName("com.sun.management.ThreadMXBean"); if (clazz != null) { this.threadMxBeanWrapper = new SecureScriptThreadMxBeanWrapper(); } } catch (ClassNotFoundException cnfe) { LOGGER.warn("com.sun.management.ThreadMXBean was not found on the classpath. " + "This means that the limiting the memory usage for a script will NOT work."); } } } public int getMaxStackDepth() { return maxStackDepth; } public void setMaxStackDepth(int maxStackDepth) { this.maxStackDepth = maxStackDepth; } }