View Javadoc

1   package delight.nashornsandbox.internal;
2   
3   import com.google.common.base.Objects;
4   import delight.async.Value;
5   import delight.nashornsandbox.NashornSandbox;
6   import delight.nashornsandbox.exceptions.ScriptCPUAbuseException;
7   import delight.nashornsandbox.internal.BeautifyJs;
8   import delight.nashornsandbox.internal.InterruptTest;
9   import delight.nashornsandbox.internal.MonitorThread;
10  import delight.nashornsandbox.internal.SandboxClassFilter;
11  import java.util.HashSet;
12  import java.util.Random;
13  import java.util.Set;
14  import java.util.concurrent.ExecutorService;
15  import javax.script.ScriptEngine;
16  import javax.script.ScriptException;
17  import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
18  import jdk.nashorn.api.scripting.ScriptObjectMirror;
19  import org.eclipse.xtend2.lib.StringConcatenation;
20  import org.eclipse.xtext.xbase.lib.Exceptions;
21  
22  @SuppressWarnings("all")
23  public class NashornSandboxImpl implements NashornSandbox {
24    private final Set<String> allowedClasses;
25    
26    private ScriptEngine scriptEngine;
27    
28    private Long maxCPUTimeInMs = Long.valueOf(0L);
29    
30    private ExecutorService exectuor;
31    
32    public void assertScriptEngine() {
33      try {
34        boolean _notEquals = (!Objects.equal(this.scriptEngine, null));
35        if (_notEquals) {
36          return;
37        }
38        final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
39        SandboxClassFilter _sandboxClassFilter = new SandboxClassFilter(this.allowedClasses);
40        ScriptEngine _scriptEngine = factory.getScriptEngine(_sandboxClassFilter);
41        this.scriptEngine = _scriptEngine;
42        this.scriptEngine.eval("var window = {};");
43        this.scriptEngine.eval(BeautifyJs.CODE);
44      } catch (Throwable _e) {
45        throw Exceptions.sneakyThrow(_e);
46      }
47    }
48    
49    @Override
50    public Object eval(final String js) {
51      try {
52        Object _xblockexpression = null;
53        {
54          this.assertScriptEngine();
55          if (((this.maxCPUTimeInMs).longValue() == 0)) {
56            return this.scriptEngine.eval(js);
57          }
58          Object _xsynchronizedexpression = null;
59          synchronized (this) {
60            Object _xblockexpression_1 = null;
61            {
62              final Value<Object> resVal = new Value<Object>(null);
63              final Value<Throwable> exceptionVal = new Value<Throwable>(null);
64              final MonitorThread monitorThread = new MonitorThread(((this.maxCPUTimeInMs).longValue() * 1000000));
65              boolean _equals = Objects.equal(this.exectuor, null);
66              if (_equals) {
67                throw new IllegalStateException(
68                  "When a CPU time limit is set, an executor needs to be provided by calling .setExecutor(...)");
69              }
70              final Object monitor = new Object();
71              final Runnable _function = new Runnable() {
72                @Override
73                public void run() {
74                  try {
75                    boolean _contains = js.contains("intCheckForInterruption");
76                    if (_contains) {
77                      throw new IllegalArgumentException(
78                        "Script contains the illegal string [intCheckForInterruption]");
79                    }
80                    Object _eval = NashornSandboxImpl.this.scriptEngine.eval("window.js_beautify;");
81                    final ScriptObjectMirror jsBeautify = ((ScriptObjectMirror) _eval);
82                    Object _call = jsBeautify.call("beautify", js);
83                    final String beautifiedJs = ((String) _call);
84                    Random _random = new Random();
85                    int _nextInt = _random.nextInt();
86                    final int randomToken = Math.abs(_nextInt);
87                    StringConcatenation _builder = new StringConcatenation();
88                    _builder.append("var InterruptTest = Java.type(\'");
89                    String _name = InterruptTest.class.getName();
90                    _builder.append(_name, "");
91                    _builder.append("\');");
92                    _builder.newLineIfNotEmpty();
93                    _builder.append("var isInterrupted = InterruptTest.isInterrupted;");
94                    _builder.newLine();
95                    _builder.append("var intCheckForInterruption");
96                    _builder.append(randomToken, "");
97                    _builder.append(" = function() {");
98                    _builder.newLineIfNotEmpty();
99                    _builder.append("\t");
100                   _builder.append("if (isInterrupted()) {");
101                   _builder.newLine();
102                   _builder.append("\t    ");
103                   _builder.append("throw new Error(\'Interrupted");
104                   _builder.append(randomToken, "\t    ");
105                   _builder.append("\')");
106                   _builder.newLineIfNotEmpty();
107                   _builder.append("\t");
108                   _builder.append("}");
109                   _builder.newLine();
110                   _builder.append("};");
111                   _builder.newLine();
112                   String _replaceAll = beautifiedJs.replaceAll(";\\n", ((";intCheckForInterruption" + Integer.valueOf(randomToken)) + "();\n"));
113                   String _replace = _replaceAll.replace(") {", ((") {intCheckForInterruption" + Integer.valueOf(randomToken)) + "();\n"));
114                   final String securedJs = (_builder.toString() + _replace);
115                   final Thread mainThread = Thread.currentThread();
116                   Thread _currentThread = Thread.currentThread();
117                   monitorThread.setThreadToMonitor(_currentThread);
118                   final Runnable _function = new Runnable() {
119                     @Override
120                     public void run() {
121                       mainThread.interrupt();
122                     }
123                   };
124                   monitorThread.setOnInvalidHandler(_function);
125                   monitorThread.start();
126                   try {
127                     final Object res = NashornSandboxImpl.this.scriptEngine.eval(securedJs);
128                     resVal.set(res);
129                   } catch (final Throwable _t) {
130                     if (_t instanceof ScriptException) {
131                       final ScriptException e = (ScriptException)_t;
132                       String _message = e.getMessage();
133                       boolean _contains_1 = _message.contains(("Interrupted" + Integer.valueOf(randomToken)));
134                       if (_contains_1) {
135                         monitorThread.notifyOperationInterrupted();
136                       }
137                     } else {
138                       throw Exceptions.sneakyThrow(_t);
139                     }
140                   } finally {
141                     monitorThread.stopMonitor();
142                     synchronized (monitor) {
143                       monitor.notify();
144                     }
145                   }
146                 } catch (final Throwable _t_1) {
147                   if (_t_1 instanceof Throwable) {
148                     final Throwable t = (Throwable)_t_1;
149                     exceptionVal.set(t);
150                     monitorThread.stopMonitor();
151                     synchronized (monitor) {
152                       monitor.notify();
153                     }
154                   } else {
155                     throw Exceptions.sneakyThrow(_t_1);
156                   }
157                 }
158               }
159             };
160             this.exectuor.execute(_function);
161             synchronized (monitor) {
162               monitor.wait();
163             }
164             boolean _isCPULimitExceeded = monitorThread.isCPULimitExceeded();
165             if (_isCPULimitExceeded) {
166               String notGraceful = "";
167               boolean _gracefullyInterrputed = monitorThread.gracefullyInterrputed();
168               boolean _not = (!_gracefullyInterrputed);
169               if (_not) {
170                 notGraceful = " The operation could not be gracefully interrupted.";
171               }
172               Throwable _get = exceptionVal.get();
173               throw new ScriptCPUAbuseException(
174                 ((("Script used more than the allowed [" + this.maxCPUTimeInMs) + " ms] of CPU time. ") + notGraceful), _get);
175             }
176             Throwable _get_1 = exceptionVal.get();
177             boolean _notEquals = (!Objects.equal(_get_1, null));
178             if (_notEquals) {
179               throw exceptionVal.get();
180             }
181             _xblockexpression_1 = resVal.get();
182           }
183           _xsynchronizedexpression = _xblockexpression_1;
184         }
185         _xblockexpression = _xsynchronizedexpression;
186       }
187       return _xblockexpression;
188     } catch (Throwable _e) {
189       throw Exceptions.sneakyThrow(_e);
190     }
191   }
192   
193   @Override
194   public NashornSandbox setMaxCPUTime(final long limit) {
195     NashornSandboxImpl _xblockexpression = null;
196     {
197       this.maxCPUTimeInMs = Long.valueOf(limit);
198       _xblockexpression = this;
199     }
200     return _xblockexpression;
201   }
202   
203   @Override
204   public NashornSandbox allow(final Class<?> clazz) {
205     NashornSandboxImpl _xblockexpression = null;
206     {
207       String _name = clazz.getName();
208       this.allowedClasses.add(_name);
209       this.scriptEngine = null;
210       _xblockexpression = this;
211     }
212     return _xblockexpression;
213   }
214   
215   @Override
216   public NashornSandbox setExecutor(final ExecutorService executor) {
217     NashornSandboxImpl _xblockexpression = null;
218     {
219       this.exectuor = executor;
220       _xblockexpression = this;
221     }
222     return _xblockexpression;
223   }
224   
225   @Override
226   public ExecutorService getExecutor() {
227     return this.exectuor;
228   }
229   
230   public NashornSandboxImpl() {
231     HashSet<String> _hashSet = new HashSet<String>();
232     this.allowedClasses = _hashSet;
233     this.allow(InterruptTest.class);
234   }
235 }