/* 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;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.activiti.engine.cfg.AbstractProcessEngineConfigurator;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.parse.BpmnParseHandler;
import org.activiti.scripting.secure.behavior.SecureJavascriptTaskParseHandler;
import org.activiti.scripting.secure.impl.SecureScriptClassShutter;
import org.activiti.scripting.secure.impl.SecureScriptContextFactory;
import org.activiti.tasks.secure.impl.ClassWhitelister;
import org.activiti.tasks.secure.impl.DefaultClassWhitelister;
import org.mozilla.javascript.ContextFactory;
/**
* @author Joram Barrez
* @author Bassam Al-Sarori
*/
public class SecureJavascriptConfigurator extends AbstractProcessEngineConfigurator {
/* Rhino's global context factory */
public static SecureScriptClassShutter secureScriptClassShutter;
/* Rhino's global context factory */
public static SecureScriptContextFactory secureScriptContextFactory;
/**
* When true, by default all classes will be blacklisted and all classes that
* want to be used will need to be whitelisted individually.
*/
protected boolean enableClassWhiteListing;
/**
* Whitelisted classes for script execution.
*
* By default empty (i.e. everything is blacklisted)
*
* From the Rhino ClassShutter javadoc:
*
* Due to the fact that there is no package reflection in Java, this method
* will also be called with package names. There is no way for Rhino to tell
* if "Packages.a.b" is a package name or a class that doesn't exist. What
* Rhino does is attempt to load each segment of "Packages.a.b.c": It first
* attempts to load class "a", then attempts to load class "a.b", then finally
* attempts to load class "a.b.c". On a Rhino installation without any
* ClassShutter set, and without any of the above classes, the expression
* "Packages.a.b.c" will result in a [JavaPackage a.b.c] and not an error.
*
* With ClassShutter supplied, Rhino will first call visibleToScripts before
* attempting to look up the class name. If visibleToScripts returns false,
* the class name lookup is not performed and subsequent Rhino execution
* assumes the class is not present. So for "java.lang.System.out.println" the
* lookup of "java.lang.System" is skipped and thus Rhino assumes that
* "java.lang.System" doesn't exist. So then for "java.lang.System.out", Rhino
* attempts to load the class "java.lang.System.out" because it assumes that
* "java.lang.System" is a package name.
*/
protected Set<String> whiteListedClasses;
/**
* The maximum time (in ms) that a script is allowed to execute before
* stopping it.
*
* By default disabled.
*/
protected long maxScriptExecutionTime = -1L;
/**
* Limits the stack depth while calling functions within the script.
*
* By default disabled.
*/
protected int maxStackDepth = -1;
/**
* Limits the memory used by the script. If the memory limit is reached, an
* exception will be thrown and the script will be stopped.
*/
protected long maxMemoryUsed = -1L;
/**
* The maximum script execution time and memory usage is implemented using a
* callback that is called every x instructions of the script. Note that these
* are not script instructions, but java byte code instructions (which means
* one script line can be thousands of byte code instructions!).
*/
protected int nrOfInstructionsBeforeStateCheckCallback = 100;
/**
* By default, no script optimization is applied. Change this setting to
* change the Rhino script optimization level. Note: some simple performance
* tests seem to indicate that for basic scripts upping this value actually
* has worse results ...
*
* Note: if using a maxStackDepth setting, the script optimization level will
* always be -1.
*/
protected int scriptOptimizationLevel = -1;
/**
* An implementation of {@link ClassWhitelister}
* if set will be used to determine whether
* a class is visible for scripts or not
*/
protected ClassWhitelister classWhitelister;
@Override
public void beforeInit(ProcessEngineConfigurationImpl processEngineConfiguration) {
// Initialize the Rhino context factory (needs to be done once)
if (secureScriptContextFactory == null) {
initSecureScriptContextFactory();
}
// Init parse handler that will set the secure javascript task to the activity
List<BpmnParseHandler> customDefaultBpmnParseHandlers = processEngineConfiguration.getCustomDefaultBpmnParseHandlers();
if (customDefaultBpmnParseHandlers == null) {
customDefaultBpmnParseHandlers = new ArrayList<BpmnParseHandler>();
processEngineConfiguration.setCustomDefaultBpmnParseHandlers(customDefaultBpmnParseHandlers);
}
customDefaultBpmnParseHandlers.add(new SecureJavascriptTaskParseHandler());
}
protected synchronized void initSecureScriptContextFactory() {
if (secureScriptContextFactory == null) {
secureScriptContextFactory = new SecureScriptContextFactory();
secureScriptContextFactory.setOptimizationLevel(getScriptOptimizationLevel());
if (isEnableClassWhiteListing() || getWhiteListedClasses() != null
|| classWhitelister != null) {
if (classWhitelister == null) {
classWhitelister = new DefaultClassWhitelister();
if (getWhiteListedClasses() != null && getWhiteListedClasses().size() > 0) {
((DefaultClassWhitelister)classWhitelister).setWhiteListedClasses(getWhiteListedClasses());
}
}
secureScriptClassShutter = new SecureScriptClassShutter();
secureScriptClassShutter.setClassWhitelister(classWhitelister);
secureScriptContextFactory.setClassShutter(secureScriptClassShutter);
}
if (getMaxScriptExecutionTime() > 0L) {
secureScriptContextFactory.setMaxScriptExecutionTime(getMaxScriptExecutionTime());
}
if (getMaxMemoryUsed() > 0L) {
secureScriptContextFactory.setMaxMemoryUsed(getMaxMemoryUsed());
}
if (getMaxStackDepth() > 0) {
secureScriptContextFactory.setMaxStackDepth(getMaxStackDepth());
}
if (getMaxScriptExecutionTime() > 0L || getMaxMemoryUsed() > 0L) {
secureScriptContextFactory.setObserveInstructionCount(getNrOfInstructionsBeforeStateCheckCallback());
}
ContextFactory.initGlobal(secureScriptContextFactory);
}
}
public boolean isEnableClassWhiteListing() {
return enableClassWhiteListing;
}
public SecureJavascriptConfigurator setEnableClassWhiteListing(boolean enableClassWhiteListing) {
this.enableClassWhiteListing = enableClassWhiteListing;
return this;
}
public Set<String> getWhiteListedClasses() {
return whiteListedClasses;
}
public SecureJavascriptConfigurator setWhiteListedClasses(Set<String> whiteListedClasses) {
this.whiteListedClasses = whiteListedClasses;
return this;
}
public SecureJavascriptConfigurator addWhiteListedClass(String whiteListedClass) {
if (this.whiteListedClasses == null) {
this.whiteListedClasses = new HashSet<String>();
}
this.whiteListedClasses.add(whiteListedClass);
return this;
}
public long getMaxScriptExecutionTime() {
return maxScriptExecutionTime;
}
public SecureJavascriptConfigurator setMaxScriptExecutionTime(long maxScriptExecutionTime) {
this.maxScriptExecutionTime = maxScriptExecutionTime;
return this;
}
public int getNrOfInstructionsBeforeStateCheckCallback() {
return nrOfInstructionsBeforeStateCheckCallback;
}
public SecureJavascriptConfigurator setNrOfInstructionsBeforeStateCheckCallback(int nrOfInstructionsBeforeStateCheckCallback) {
this.nrOfInstructionsBeforeStateCheckCallback = nrOfInstructionsBeforeStateCheckCallback;
return this;
}
public int getMaxStackDepth() {
return maxStackDepth;
}
public SecureJavascriptConfigurator setMaxStackDepth(int maxStackDepth) {
this.maxStackDepth = maxStackDepth;
return this;
}
public long getMaxMemoryUsed() {
return maxMemoryUsed;
}
public SecureJavascriptConfigurator setMaxMemoryUsed(long maxMemoryUsed) {
this.maxMemoryUsed = maxMemoryUsed;
return this;
}
public int getScriptOptimizationLevel() {
return scriptOptimizationLevel;
}
public SecureJavascriptConfigurator setScriptOptimizationLevel(int scriptOptimizationLevel) {
this.scriptOptimizationLevel = scriptOptimizationLevel;
return this;
}
public SecureScriptContextFactory getSecureScriptContextFactory() {
return secureScriptContextFactory;
}
public static SecureScriptClassShutter getSecureScriptClassShutter() {
return secureScriptClassShutter;
}
public ClassWhitelister getClassWhitelister() {
return classWhitelister;
}
public void setClassWhitelister(ClassWhitelister classWhitelister) {
this.classWhitelister = classWhitelister;
}
}