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 }