Skip to content

Commit 5de8560

Browse files
committed
feat: adds enableSandboxMode method to enable sandbox mode
1 parent 3716a6c commit 5de8560

4 files changed

Lines changed: 115 additions & 4 deletions

File tree

src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,37 @@ public Map<String, Object> requireScript(final String path) throws IOException {
448448
}
449449
}
450450

451+
/**
452+
* Configure the evaluator into sandbox mode for security, it means:
453+
* <ul>
454+
* <li>Disable syntax feature: Module, NewInstance, StaticMethods and InternalVars,</li>
455+
* <li>Disable reflection invocation by function missing,</li>
456+
* <li>Set the single maximum loop counter to 65535,</li>
457+
* <li>Set ALLOWED_CLASS_SET and ASSIGNABLE_ALLOWED_CLASS_SET to be empty, disable all classes to
458+
* be accessed via static fields or methods,</li>
459+
* <li>Set the EVAL_TIMEOUT_MS to be 1000 milliseconds(1 second), which means the execution
460+
* timeout.
461+
* <li>
462+
* </ul>
463+
*
464+
* For more information on security, please refer to the
465+
* <a href="https://www.yuque.com/boyan-avfmj/aviatorscript/ou23gy#elOSu">documentation</a>
466+
*
467+
* @since 5.4.3
468+
*
469+
*/
470+
public void enableSandboxMode() {
471+
disableFeature(Feature.Module);
472+
disableFeature(Feature.NewInstance);
473+
disableFeature(Feature.InternalVars);
474+
disableFeature(Feature.StaticMethods);
475+
setFunctionMissing(null);
476+
setOption(Options.MAX_LOOP_COUNT, 65535);
477+
setOption(Options.ALLOWED_CLASS_SET, Collections.emptySet());
478+
setOption(Options.ASSIGNABLE_ALLOWED_CLASS_SET, Collections.emptySet());
479+
setOption(Options.EVAL_TIMEOUT_MS, 1000L);
480+
}
481+
451482
/**
452483
* Adds a module class and import it's public static methods as module's exports into module
453484
* cache, return the exports map.
@@ -701,8 +732,10 @@ public void setOption(final Options opt, final Object val) {
701732
* @param feature
702733
*/
703734
public void enableFeature(final Feature feature) {
704-
this.options.get(Options.FEATURE_SET).featureSet.add(feature);
705-
this.options.get(Options.FEATURE_SET).featureSet.addAll(feature.getPrequires());
735+
Set<Feature> featureSet = new HashSet<>(this.options.get(Options.FEATURE_SET).featureSet);
736+
featureSet.add(feature);
737+
featureSet.addAll(feature.getPrequires());
738+
setOption(Options.FEATURE_SET, featureSet);
706739
loadFeatureFunctions();
707740
}
708741

@@ -733,10 +766,12 @@ public boolean isFeatureEnabled(final Feature feature) {
733766
* @param feature
734767
*/
735768
public void disableFeature(final Feature feature) {
736-
this.options.get(Options.FEATURE_SET).featureSet.remove(feature);
769+
Set<Feature> featureSet = new HashSet<>(this.options.get(Options.FEATURE_SET).featureSet);
770+
featureSet.remove(feature);
737771
for (AviatorFunction fn : feature.getFunctions()) {
738772
this.removeFunction(fn);
739773
}
774+
this.setOption(Options.FEATURE_SET, featureSet);
740775
loadFeatureFunctions();
741776
}
742777

src/main/java/com/googlecode/aviator/runtime/type/AviatorString.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.googlecode.aviator.exception.ExpressionRuntimeException;
2727
import com.googlecode.aviator.runtime.RuntimeUtils;
2828
import com.googlecode.aviator.utils.Constants;
29+
import com.googlecode.aviator.utils.Env;
2930
import com.googlecode.aviator.utils.TypeUtils;
3031

3132
/**
@@ -177,7 +178,8 @@ public String getLexeme(final Map<String, Object> env, final boolean warnOnCompi
177178
return this.lexeme;
178179
}
179180
StringSegments segs = null;
180-
BaseExpression exp = (BaseExpression) (env == null ? null : env.get(Constants.EXP_VAR));
181+
BaseExpression exp = (BaseExpression) ((env == null || !(env instanceof Env)) ? null
182+
: ((Env) env).getExpression());
181183
if (exp != null) {
182184
segs = exp.getStringSegements(this.lexeme, this.lineNo);
183185
} else {

src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceCompatibleUnitTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package com.googlecode.aviator;
22

33
import static com.googlecode.aviator.TestUtils.assertEquals;
4+
import static org.junit.Assert.assertNull;
45
import static org.junit.Assert.fail;
56
import java.math.MathContext;
67
import org.junit.Before;
78
import org.junit.Test;
9+
import com.googlecode.aviator.exception.ExpressionRuntimeException;
10+
import com.googlecode.aviator.exception.FunctionNotFoundException;
811
import com.googlecode.aviator.exception.UnsupportedFeatureException;
912

1013
public class AviatorEvaluatorInstanceCompatibleUnitTest extends AviatorEvaluatorInstanceUnitTest {
@@ -22,6 +25,39 @@ public void testIssue549() {
2225
// ignore
2326
}
2427

28+
@Test
29+
public void testSandboxMode() {
30+
this.instance.enableSandboxMode();
31+
try {
32+
this.instance.execute("new java.util.Date()");
33+
} catch (UnsupportedFeatureException e) {
34+
// ignore
35+
}
36+
37+
try {
38+
this.instance.execute("Math.abs(-1)");
39+
} catch (FunctionNotFoundException e) {
40+
// ignore
41+
}
42+
43+
try {
44+
this.instance.execute("System.exit(1)");
45+
} catch (FunctionNotFoundException e) {
46+
// ignore
47+
}
48+
49+
try {
50+
this.instance.execute("Math.PI");
51+
} catch (ExpressionRuntimeException e) {
52+
// ignore
53+
}
54+
try {
55+
assertNull(this.instance.execute("__env__"));
56+
} catch (UnsupportedFeatureException e) {
57+
58+
}
59+
}
60+
2561
@Override
2662
@Test(expected = UnsupportedFeatureException.class)
2763
public void testMaxLoopCount() {

src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceUnitTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.googlecode.aviator.exception.CompileExpressionErrorException;
3939
import com.googlecode.aviator.exception.ExpressionRuntimeException;
4040
import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
41+
import com.googlecode.aviator.exception.FunctionNotFoundException;
4142
import com.googlecode.aviator.exception.TimeoutException;
4243
import com.googlecode.aviator.exception.UnsupportedFeatureException;
4344
import com.googlecode.aviator.lexer.token.OperatorType;
@@ -61,6 +62,43 @@ public void setup() {
6162
this.instance.setOption(Options.EVAL_TIMEOUT_MS, 100);
6263
}
6364

65+
@Test
66+
public void testSandboxMode() {
67+
this.instance.enableSandboxMode();
68+
try {
69+
this.instance.execute("new java.util.Date()");
70+
} catch (UnsupportedFeatureException e) {
71+
// ignore
72+
}
73+
74+
try {
75+
this.instance.execute("Math.abs(-1)");
76+
} catch (FunctionNotFoundException e) {
77+
// ignore
78+
}
79+
80+
try {
81+
this.instance.execute("System.exit(1)");
82+
} catch (FunctionNotFoundException e) {
83+
// ignore
84+
}
85+
86+
try {
87+
this.instance.execute("Math.PI");
88+
} catch (ExpressionRuntimeException e) {
89+
// ignore
90+
}
91+
try {
92+
this.instance.execute("while(true) {}");
93+
} catch (ExpressionRuntimeException e) {
94+
// ignore
95+
}
96+
try {
97+
assertNull(this.instance.execute("__env__"));
98+
} catch (UnsupportedFeatureException e) {
99+
100+
}
101+
}
64102

65103
@Test
66104
public void testIssue549() {

0 commit comments

Comments
 (0)